﻿///////////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2023, ООО 1С-Софт
// Все права защищены. Эта программа и сопроводительные материалы предоставляются 
// в соответствии с условиями лицензии Attribution 4.0 International (CC BY 4.0)
// Текст лицензии доступен по ссылке:
// https://creativecommons.org/licenses/by/4.0/legalcode
///////////////////////////////////////////////////////////////////////////////////////////////////////

#Область ПрограммныйИнтерфейс

////////////////////////////////////////////////////////////////////////////////
// Процедуры и функции для использования в обработчиках обновления.
//

// Записывает изменения в переданном объекте.
// Для использования в обработчиках обновления.
//
// Параметры:
//   Данные                            - Произвольный - объект, набор записей или менеджер константы, который
//                                                      необходимо записать.
//   РегистрироватьНаУзлахПлановОбмена - Булево       - включает регистрацию на узлах планов обмена при записи объекта.
//   ВключитьБизнесЛогику              - Булево       - включает бизнес-логику при записи объекта.
//
Процедура ЗаписатьДанные(Знач Данные, Знач РегистрироватьНаУзлахПлановОбмена = Неопределено, 
	Знач ВключитьБизнесЛогику = Ложь) Экспорт
	
	Данные.ОбменДанными.Загрузка = Не ВключитьБизнесЛогику;
	Данные.ДополнительныеСвойства.Вставить("РегистрироватьНаУзлахПлановОбменаПриОбновленииИБ", РегистрироватьНаУзлахПлановОбмена);
	
	Если РегистрироватьНаУзлахПлановОбмена = Неопределено
		Или Не РегистрироватьНаУзлахПлановОбмена Тогда
		Данные.ОбменДанными.Получатели.АвтоЗаполнение = Ложь;
	КонецЕсли;
	
	Данные.Записать();
	
	Если ОбъектРегистрируетсяНаПланеОбмена(Данные) Тогда
		ОтметитьВыполнениеОбработки(Данные);
	КонецЕсли;
	
КонецПроцедуры

// Записывает изменения в переданном объекте ссылочного типа.
// Для использования в обработчиках обновления.
//
// Параметры:
//   Объект                            - Произвольный - записываемый объект ссылочного типа. Например, СправочникОбъект.
//   РегистрироватьНаУзлахПлановОбмена - Булево       - включает регистрацию на узлах планов обмена при записи объекта.
//   ВключитьБизнесЛогику              - Булево       - включает бизнес-логику при записи объекта.
//   ДокументРежимЗаписи               - РежимЗаписиДокумента - имеет смысл только для данных типа ДокументОбъект - режим
//                                                            записи документа.
//											Если параметр не передан, то документ записывается в режиме "Запись".
//
Процедура ЗаписатьОбъект(Знач Объект, Знач РегистрироватьНаУзлахПлановОбмена = Неопределено, 
	Знач ВключитьБизнесЛогику = Ложь, ДокументРежимЗаписи = Неопределено) Экспорт
	
	Объект.ДополнительныеСвойства.Вставить("РегистрироватьНаУзлахПлановОбменаПриОбновленииИБ", РегистрироватьНаУзлахПлановОбмена);
	Объект.ОбменДанными.Загрузка = Не ВключитьБизнесЛогику;
	
	Если РегистрироватьНаУзлахПлановОбмена = Неопределено
		Или Не РегистрироватьНаУзлахПлановОбмена
		И Не Объект.ЭтоНовый() Тогда
		Объект.ОбменДанными.Получатели.АвтоЗаполнение = Ложь;
	КонецЕсли;
	
	Если ДокументРежимЗаписи <> Неопределено Тогда
		Если ТипЗнч(ДокументРежимЗаписи) <> Тип("РежимЗаписиДокумента") Тогда
			ТекстИсключения = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'Неправильный тип параметра %1'"),
				"ДокументРежимЗаписи");
			ВызватьИсключение ТекстИсключения;
		КонецЕсли;
		Объект.ОбменДанными.Загрузка = Объект.ОбменДанными.Загрузка
			И Не ДокументРежимЗаписи = РежимЗаписиДокумента.Проведение
			И Не ДокументРежимЗаписи = РежимЗаписиДокумента.ОтменаПроведения;
		Объект.Записать(ДокументРежимЗаписи);
	Иначе
		Объект.Записать();
	КонецЕсли;
	
	Если ОбъектРегистрируетсяНаПланеОбмена(Объект) Тогда
		ОтметитьВыполнениеОбработки(Объект);
	КонецЕсли;
	
КонецПроцедуры

// Записывает изменения в переданном наборе записей.
// Для использования в обработчиках обновления.
//
// Параметры:
//   НаборЗаписей - РегистрСведенийНаборЗаписей
//                - РегистрНакопленияНаборЗаписей
//                - РегистрБухгалтерииНаборЗаписей
//                - РегистрРасчетаНаборЗаписей - набор записей, который необходимо записать.
//   Замещать     - Булево - определяет режим замещения существующей записи в соответствии с
//                           текущими установками отбора. Истина - перед записью существующие
//                           записи будут удалены. Ложь - записи будут дописаны к уже существующим
//                           в информационной базе записям.
//   РегистрироватьНаУзлахПлановОбмена - Булево       - включает регистрацию на узлах планов обмена при записи объекта.
//   ВключитьБизнесЛогику              - Булево       - включает бизнес-логику при записи объекта.
//
Процедура ЗаписатьНаборЗаписей(Знач НаборЗаписей, Замещать = Истина, Знач РегистрироватьНаУзлахПлановОбмена = Неопределено,
	Знач ВключитьБизнесЛогику = Ложь) Экспорт
	
	НаборЗаписей.ДополнительныеСвойства.Вставить("РегистрироватьНаУзлахПлановОбменаПриОбновленииИБ", РегистрироватьНаУзлахПлановОбмена);
	НаборЗаписей.ОбменДанными.Загрузка = Не ВключитьБизнесЛогику;
	
	Если РегистрироватьНаУзлахПлановОбмена = Неопределено 
		Или Не РегистрироватьНаУзлахПлановОбмена Тогда
		НаборЗаписей.ОбменДанными.Получатели.АвтоЗаполнение = Ложь;
	КонецЕсли;
	
	НаборЗаписей.Записать(Замещать);
	
	Если ОбъектРегистрируетсяНаПланеОбмена(НаборЗаписей) Тогда
		ОтметитьВыполнениеОбработки(НаборЗаписей);
	КонецЕсли;
	
КонецПроцедуры

// Удаляет переданный объект.
// Для использования в обработчиках обновления.
//
// Параметры:
//  Данные                            - Произвольный - объект, который необходимо удалить.
//  РегистрироватьНаУзлахПлановОбмена - Булево       - включает регистрацию на узлах планов обмена при записи объекта.
//  ВключитьБизнесЛогику              - Булево       - включает бизнес-логику при записи объекта.
//
Процедура УдалитьДанные(Знач Данные, Знач РегистрироватьНаУзлахПлановОбмена = Неопределено, 
	Знач ВключитьБизнесЛогику = Ложь) Экспорт
	
	Данные.ДополнительныеСвойства.Вставить("РегистрироватьНаУзлахПлановОбменаПриОбновленииИБ", РегистрироватьНаУзлахПлановОбмена);
	
	Данные.ОбменДанными.Загрузка = Не ВключитьБизнесЛогику;
	Если РегистрироватьНаУзлахПлановОбмена = Неопределено 
		Или Не РегистрироватьНаУзлахПлановОбмена Тогда
		Данные.ОбменДанными.Получатели.АвтоЗаполнение = Ложь;
	КонецЕсли;
	
	Данные.Удалить();
	
КонецПроцедуры

// Возвращает строковую константу для формирования сообщений журнала регистрации.
//
// Возвращаемое значение:
//   Строка - текст события журнала регистрации.
//
Функция СобытиеЖурналаРегистрации() Экспорт
	
	Возврат ОбновлениеИнформационнойБазыСлужебный.СобытиеЖурналаРегистрации();
	
КонецФункции

////////////////////////////////////////////////////////////////////////////////
// Процедуры и функции для проверки доступности объекта при выполнении отложенного обновления.
//

// Вызывает исключение или блокирует форму от редактирования, если
// имеются незавершенные отложенные обработчики обновления,
// которые в данный момент обрабатывают переданный объект Данные.
//
// При вызове из отложенного обработчика обновления (случай проверки в программном интерфейсе)
// проверка не выполняется, если не указан параметр ИмяОтложенногоОбработчика, так как
// предполагается, что порядок обновления уже учтен при построении очередей.
//
// Параметры:
//  Данные  - СправочникОбъект
//          - ДокументОбъект
//          - ПланВидовХарактеристикОбъект
//          - ПланСчетовОбъект
//          - ПланВидовРасчетаОбъект
//          - РегистрСведенийНаборЗаписей
//          - РегистрНакопленияНаборЗаписей
//          - РегистрБухгалтерииНаборЗаписей
//          - РегистрРасчетаНаборЗаписей
//          - ЛюбаяСсылка
//          - ДанныеФормыСтруктура 
//          - Строка - ссылка на объект, сам объект, набор записей или полное имя объекта
//                       метаданных, обработку которого необходимо проверить.
//  Форма  - ФормаКлиентскогоПриложения - если объект не обработан, то у переданной формы
//           будет установлено свойство ТолькоПросмотр. Если форма не была
//           передана, то будет вызвано исключение.
//
//  ИмяОтложенногоОбработчика - Строка - если заполнено, тогда при вызове из другого отложенного обработчика
//           проверяется, что указанный отложенный обработчик имеет номер очереди меньше, чем текущий.
//           Если это не так, тогда вызывается исключение о недопустимости использования
//           программного интерфейса, указанного в параметре ИмяПроцедурыПрограммногоИнтерфейса.
//
//  ИмяПроцедурыПрограммногоИнтерфейса - Строка - имя процедуры программного интерфейса,
//           которое выводится в тексте исключения, вызываемого при проверке номера очереди
//           отложенного обработчика обновления, указанного в параметре ИмяОтложенногоОбработчика.
//
//  Пример:
//   Блокировка формы объекта в обработчике ПриСозданииНаСервере модуля формы:
//   ОбновлениеИнформационнойБазы.ПроверитьОбъектОбработан(Объект, ЭтотОбъект);
//
//   Блокировка записи объекта в обработчике ПередЗаписью модуля объекта (набора записей):
//   ОбновлениеИнформационнойБазы.ПроверитьОбъектОбработан(ЭтотОбъект);
//
//   Проверить, что обновлен конкретный объект и вызвать исключение о недопустимости вызова
//   процедуры ЭлектроннаяПодпись.ОбновитьПодпись, если он еще не обработан указанным обработчиком
//   Справочник.ЭлектронныеПодписи.ОбработатьДанныеДляПереходаНаНовуюВерсию:
//
//   ОбновлениеИнформационнойБазы.ПроверитьОбъектОбработан(ПодписанныйОбъект,,
//      "Справочник.ЭлектронныеПодписи.ОбработатьДанныеДляПереходаНаНовуюВерсию",
//      "ЭлектроннаяПодпись.ОбновитьПодпись");
//
//   Проверить и вызвать исключение, если обновлены не все объекты требуемого типа:
//   ОбновлениеИнформационнойБазы.ПроверитьОбъектОбработан("Документ.ЗаказПокупателя"); 
//
Процедура ПроверитьОбъектОбработан(Данные, Форма = Неопределено, ИмяОтложенногоОбработчика = "", ИмяПроцедурыПрограммногоИнтерфейса = "") Экспорт
	
	Если Не ЭтоВызовИзОбработчикаОбновления() Тогда
		Результат = ОбъектОбработан(Данные);
		Если Результат.Обработан Тогда
			Возврат;
		КонецЕсли;
			
		Если Форма = Неопределено Тогда
			ВызватьИсключение Результат.ТекстИсключения;
		КонецЕсли;
		
		Форма.ТолькоПросмотр = Истина;
		Форма.Команды.Добавить("ОбновлениеВерсииИБ_ОбъектЗаблокирован");
		ОбщегоНазначения.СообщитьПользователю(Результат.ТекстИсключения);
		Возврат;
	КонецЕсли;
	
	Если Не ЗначениеЗаполнено(ИмяОтложенногоОбработчика) Тогда
		Возврат;
	КонецЕсли;
	
	Если ИмяОтложенногоОбработчика = ПараметрыСеанса.ПараметрыОбработчикаОбновления.ИмяОбработчика Тогда
		Возврат;
	КонецЕсли;
	
	ОчередьТребуемогоОбработчика = ОчередьОтложенногоОбработчикаОбновления(ИмяОтложенногоОбработчика);
	ОчередьТекущегоОбработчика = ПараметрыСеанса.ПараметрыОбработчикаОбновления.ОчередьОтложеннойОбработки;
	Если ОчередьТекущегоОбработчика > ОчередьТребуемогоОбработчика Тогда
		Возврат;
	КонецЕсли;
	
	ВызватьИсключение СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
		НСтр("ru = 'Недопустимо вызывать %1
		           |из обработчика обновления
		           |%2
		           |так как его номер очереди меньше или равен номеру очереди обработчика обновления
		           |%3'"),
		ИмяПроцедурыПрограммногоИнтерфейса,
		ПараметрыСеанса.ПараметрыОбработчикаОбновления.ИмяОбработчика,
		ИмяОтложенногоОбработчика);
	
КонецПроцедуры

// Проверяет, имеются ли отложенные обработчики обновления,
// которые в данный момент обрабатывают переданный объект Данные.
//
// Параметры:
//  Данные  - СправочникОбъект
//          - ДокументОбъект
//          - ПланВидовХарактеристикОбъект
//          - ПланСчетовОбъект
//          - ПланВидовРасчетаОбъект
//          - РегистрСведенийНаборЗаписей
//          - РегистрНакопленияНаборЗаписей
//          - РегистрБухгалтерииНаборЗаписей
//          - РегистрРасчетаНаборЗаписей
//          - ЛюбаяСсылка
//          - ДанныеФормыСтруктура 
//          - Строка - ссылка на объект, сам объект, набор записей или полное имя объекта метаданных,
//                     блокировку которого необходимо проверить.
//
// Возвращаемое значение:
//   Структура:
//     * Обработан       - Булево - признак того, что переданный объект обработан.
//     * ТекстИсключения - Строка - текст исключения, если объект еще не обработан,
//                         содержит список незавершенных обработчиков.
//
// Пример:
//   Проверить, что обновлены все объекты требуемого типа:
//   ВсеЗаказыОбработаны = ОбновлениеИнформационнойБазы.ОбъектОбработан("Документ.ЗаказПокупателя"); 
//
Функция ОбъектОбработан(Данные) Экспорт
	
	Результат = Новый Структура;
	Результат.Вставить("Обработан", Истина);
	Результат.Вставить("ТекстИсключения", "");
	Результат.Вставить("НевыполненныеОбработчикиСтрокой", "");
	
	Если Данные = Неопределено Тогда
		Возврат Результат;
	КонецЕсли;
	
	Если ОтложенноеОбновлениеЗавершено() Тогда
		Возврат Результат;
	КонецЕсли;
	
	СведенияОБлокируемыхОбъектах = ОбновлениеИнформационнойБазыСлужебный.СведенияОБлокируемыхОбъектах();
	
	МетаданныеИОтбор = Неопределено;
	Если ТипЗнч(Данные) = Тип("Строка") Тогда
		ПолноеИмя = Данные;
	Иначе
		МетаданныеИОтбор = МетаданныеИОтборПоДанным(Данные);
		ПолноеИмя = МетаданныеИОтбор.Метаданные.ПолноеИмя();
	КонецЕсли;
	
	РазблокированныеОбъекты = СведенияОБлокируемыхОбъектах.РазблокированныеОбъекты;
	ДоступныеДляРедактирования = РазблокированныеОбъекты[ПолноеИмя];
	Если МетаданныеИОтбор <> Неопределено
		И ДоступныеДляРедактирования <> Неопределено
		И ДоступныеДляРедактирования.Найти(МетаданныеИОтбор.Отбор) <> Неопределено Тогда
		Возврат Результат; // Объект разблокирован для редактирования.
	КонецЕсли;
	
	БлокироватьИзменение = Ложь;
	ТекстСообщения = "";
	ОбновлениеИнформационнойБазыПереопределяемый.ПриВыполненииПроверкиОбъектОбработан(ПолноеИмя, БлокироватьИзменение, ТекстСообщения);
	
	ПроверяемыйОбъект = СтрЗаменить(ПолноеИмя, ".", "");
	
	ОбработчикиОбъекта = СведенияОБлокируемыхОбъектах.БлокируемыеОбъекты[ПроверяемыйОбъект];
	Если ОбработчикиОбъекта = Неопределено Тогда
		Возврат Результат;
	КонецЕсли;
	
	Обработан = Истина;
	НезавершенныеОбработчики = Новый Массив;
	Для Каждого Обработчик Из ОбработчикиОбъекта Цикл
		СвойстваОбработчика = СведенияОБлокируемыхОбъектах.Обработчики[Обработчик];
		Если СвойстваОбработчика.Выполнен Тогда
			Обработан = Истина;
		ИначеЕсли ТипЗнч(Данные) = Тип("Строка") Тогда
			Обработан = Ложь;
		Иначе
			// АПК:488-выкл безопасный режим не требуется
			Обработан = Вычислить(СвойстваОбработчика.ПроцедураПроверки + "(МетаданныеИОтбор)");
			// АПК:488-вкл
		КонецЕсли;
		
		Результат.Обработан = Обработан И Результат.Обработан;
		
		Если Не Обработан Тогда
			НезавершенныеОбработчики.Добавить(Обработчик);
		КонецЕсли;
	КонецЦикла;
	
	Если НезавершенныеОбработчики.Количество() > 0 Тогда
		
		ЧастиИсключения = Новый Массив;
		ЧастиИсключения.Добавить(НСтр("ru = 'Действия с объектом временно запрещены, так как не завершен переход на новую версию программы.
			|Это плановый процесс, который скоро завершится.'"));
		ЧастиИсключения.Добавить(СтрСоединить(СтрРазделить(НСтр("ru = 'Для включения возможности редактирования можно нажать Еще - Разблокировать.
			|Разблокировку следует применять только в крайних случаях, так как документ может быть
			|записан некорректно.'"), Символы.ПС), " "));
		ЧастиИсключения.Добавить(НСтр("ru = 'Следующие процедуры обработки данных не завершены'"));
		
		ТекстИсключения = СтрСоединить(ЧастиИсключения, Символы.ПС + Символы.ПС) + ":";
		
		НевыполненныеОбработчикиСтрокой = "";
		Для Каждого НезавершенныйОбработчик Из НезавершенныеОбработчики Цикл
			НевыполненныеОбработчикиСтрокой = НевыполненныеОбработчикиСтрокой + Символы.ПС + НезавершенныйОбработчик;
		КонецЦикла;
		Результат.ТекстИсключения = ТекстИсключения + НевыполненныеОбработчикиСтрокой;
		Результат.НевыполненныеОбработчикиСтрокой = НевыполненныеОбработчикиСтрокой;
	КонецЕсли;
	
	Возврат Результат;
	
КонецФункции

////////////////////////////////////////////////////////////////////////////////
// Процедуры и функции для использования в отложенных обработчиках обновления
// с режимом выполнения "Параллельно".
//

// Отмечает, что переданные данные обновлены.
//
// Параметры:
//  Данные - ЛюбаяСсылка
//         - Массив
//         - РегистрСведенийНаборЗаписей, РегистрНакопленияНаборЗаписей, РегистрБухгалтерииНаборЗаписей
//         - РегистрРасчетаНаборЗаписей - данные, по которым нужно зарегистрировать изменения.
//         - ТаблицаЗначений - значения измерений независимого регистра сведений. Требования:
//                              а) все измерения регистра должны входить в основной отбор;
//                              б) в таблице должны быть только колонки, соответствующие по именам измерениям регистра,
//                                по которым ранее регистрировалась необходимость обработки;
//                              в) запись наборов в процессе обновления должна проходить с тем же отбором,
//                                что и регистрация необходимости обработки;
//                              г) в ДополнительныеПараметры нужно передать соответствующий признак и полное имя регистра.
//  ДополнительныеПараметры - см. ОбновлениеИнформационнойБазы.ДополнительныеПараметрыОтметкиОбработки.
//  Очередь - Число
//          - Неопределено - очередь обработки, в которой выполняется текущий обработчик. По умолчанию очередь передавать
//                           не нужно, т.к. она будет взята из параметров сеанса, в котором запущен обработчик обновления.
//
Процедура ОтметитьВыполнениеОбработки(Данные, ДополнительныеПараметры = Неопределено, Очередь = Неопределено) Экспорт
	Если Очередь = Неопределено Тогда
		Если ПараметрыСеанса.ПараметрыОбработчикаОбновления.РежимВыполнения <> "Отложенно"
			Или ПараметрыСеанса.ПараметрыОбработчикаОбновления.РежимВыполненияОтложенныхОбработчиков <> "Параллельно" Тогда
			Возврат;
		КонецЕсли;
		Очередь = ПараметрыСеанса.ПараметрыОбработчикаОбновления.ОчередьОтложеннойОбработки;
	КонецЕсли;
	
	Если Не ПараметрыСеанса.ПараметрыОбработчикаОбновления.ЕстьОбработанныеОбъекты Тогда
		НовыеПараметрыСеанса = ОбновлениеИнформационнойБазыСлужебный.НовыеПараметрыОбработчикаОбновления();
		
		ЗаполнитьЗначенияСвойств(НовыеПараметрыСеанса, ПараметрыСеанса.ПараметрыОбработчикаОбновления);
		НовыеПараметрыСеанса.ЕстьОбработанныеОбъекты = Истина;
		
		ИдентификаторТранзакции = Новый УникальныйИдентификатор;
		МенеджерЗаписи = РегистрыСведений.ФиксацияОбработкиДанныхОбработчиками.СоздатьМенеджерЗаписи();
		МенеджерЗаписи.ИдентификаторТранзакции = ИдентификаторТранзакции;
		МенеджерЗаписи.Записать();
		
		НовыеПараметрыСеанса.ИдентификаторТранзакции = ИдентификаторТранзакции;
		
		ПараметрыСеанса.ПараметрыОбработчикаОбновления = Новый ФиксированнаяСтруктура(НовыеПараметрыСеанса);
	КонецЕсли;
	
	КопияДанных = Данные;
	Если ДополнительныеПараметры = Неопределено Тогда
		ДополнительныеПараметры = ДополнительныеПараметрыОтметкиОбработки();
	КонецЕсли;
	
	Если (ТипЗнч(Данные) = Тип("Массив")
		Или ТипЗнч(Данные) = Тип("ТаблицаЗначений"))
		И Данные.Количество() = 0 Тогда
		
		ТекстИсключения = НСтр("ru = 'В процедуру %1 передан пустой массив. Не возможно отметить выполнение обработки.'");
		ТекстИсключения = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(ТекстИсключения, "ОбновлениеИнформационнойБазы.ОтметитьВыполнениеОбработки");
		ВызватьИсключение ТекстИсключения;
		
	КонецЕсли;
	
	Узел = ОчередьСсылкой(Очередь);
	
	Если ДополнительныеПараметры.ЭтоДвижения Тогда
		
		УдалитьРегистрациюИзмененийПодчиненногоРегистра(Узел, Данные, ДополнительныеПараметры.ПолноеИмяРегистра);
		
	ИначеЕсли ДополнительныеПараметры.ЭтоНезависимыйРегистрСведений Тогда
		
		УдалитьРегистрациюИзмененийНезависимогоРегистра(Узел, Данные, ДополнительныеПараметры.ПолноеИмяРегистра);
		
	Иначе
		Если ТипЗнч(Данные) = Тип("ОбъектМетаданных") Тогда
			ТекстИсключения = НСтр("ru = 'Не поддерживается отметка выполнения обработки обновления целиком объекта метаданных. Нужно отмечать обработку конкретных данных.'");
			ВызватьИсключение ТекстИсключения;
		КонецЕсли;
		
		Если ТипЗнч(Данные) <> Тип("Массив") Тогда
			
			ТипЗначенияОбъекта = ТипЗнч(Данные);
			МетаданныеОбъекта  = Метаданные.НайтиПоТипу(ТипЗначенияОбъекта);
			
			Если ОбщегоНазначения.ЭтоРегистрСведений(МетаданныеОбъекта)
				И МетаданныеОбъекта.РежимЗаписи = Метаданные.СвойстваОбъектов.РежимЗаписиРегистра.Независимый Тогда
				Набор = ОбщегоНазначения.МенеджерОбъектаПоПолномуИмени(МетаданныеОбъекта.ПолноеИмя()).СоздатьНаборЗаписей();
				Для Каждого ЭлементОтбора Из Данные.Отбор Цикл
					Набор.Отбор[ЭлементОтбора.Имя].Значение = ЭлементОтбора.Значение;
					Набор.Отбор[ЭлементОтбора.Имя].Использование = ЭлементОтбора.Использование;
				КонецЦикла;
				УстановитьНедостающиеОтборыВНаборе(Набор, МетаданныеОбъекта, Данные.Отбор);
			ИначеЕсли (ОбщегоНазначения.ЭтоОбъектСсылочногоТипа(МетаданныеОбъекта)
					И Не ОбщегоНазначения.ЭтоСсылка(ТипЗначенияОбъекта)
					И Данные.ЭтоНовый())
				Или ОбщегоНазначения.ЭтоКонстанта(МетаданныеОбъекта) Тогда
				Возврат;
			Иначе
				Набор = Данные;
			КонецЕсли;
			
			ЗаписатьПрогрессВыполненияОбработчика(Данные, Узел, МетаданныеОбъекта);
			ПланыОбмена.УдалитьРегистрациюИзменений(Узел, Набор);
			КопияДанных = Набор;
		Иначе
			ЗаписатьПрогрессВыполненияОбработчика(Данные, Узел, МетаданныеОбъекта);
			УдалитьРегистрациюИзмененийОбъекта(Узел, Данные);
		КонецЕсли;
		
	КонецЕсли;
	
	Если Не ОбщегоНазначения.ЭтоПодчиненныйУзелРИБ() Тогда
		РегистрыСведений.ДанныеОбработанныеВЦентральномУзлеРИБ.ОтметитьВыполнениеОбработки(Очередь, КопияДанных, ДополнительныеПараметры); 
	КонецЕсли;
	
КонецПроцедуры

// Дополнительные параметры функций ОтметитьКОбработке и ОтметитьВыполнениеОбработки.
// 
// Возвращаемое значение:
//  Структура:
//     * ЭтоДвижения - Булево - в параметре Данные функции переданы ссылки на регистраторы, по которым нужно обновить движения.
//                              Значение по умолчанию - Ложь.
//      * ПолноеИмяРегистра - Строка - полное имя регистра, по которому нужно обновить данные. Например, РегистрНакопления.ТоварыНаСкладах.
//      * ОтметитьВсеРегистраторы - Булево - необходимо отметить к обработке все проведенные документы переданного во
//                                           втором параметре типа.
//                                           В этом случае в параметре Данные процедуры можно передавать
//                                           ОбъектМетаданных:Документ или ДокументСсылка.
//      * ЭтоНезависимыйРегистрСведений - Булево - в параметре Данные функции передана таблица со значениями измерений,
//                                                 по которым нужно обновлять данные, значение по умолчанию - Ложь.
//
Функция ДополнительныеПараметрыОтметкиОбработки() Экспорт
	
	ДополнительныеПараметры = Новый Структура;
	ДополнительныеПараметры.Вставить("ЭтоДвижения", Ложь);
	ДополнительныеПараметры.Вставить("ОтметитьВсеРегистраторы", Ложь);
	ДополнительныеПараметры.Вставить("ЭтоНезависимыйРегистрСведений", Ложь);
	ДополнительныеПараметры.Вставить("ПолноеИмяРегистра", "");
	
	Возврат ДополнительныеПараметры;
	
КонецФункции

// Основные параметры процедуры ОбновлениеИнформационнойБазы.ОтметитьКОбработке,
// которые инициализируются механизмом регистрации изменений
// и не должны переопределяться в коде процедур отметки к обработке обработчиков обновления.
//
// Возвращаемое значение:
//  Структура:
//     * Очередь - Число - очередь обработки, в которой выполняется текущий обработчик.
//     * ЗаписьИзмененийДляПодчиненногоУзлаРИБСФильтрами - ЗаписьFastInfoset - параметр
//          существует, только если внедрена подсистема ОбменДанными.
//     * ПараметрыВыборки - см. ДополнительныеПараметрыВыборкиДанныхДляМногопоточнойОбработки
//
Функция ОсновныеПараметрыОтметкиКОбработке() Экспорт
	
	Параметры = Новый Структура;
	Параметры.Вставить("Очередь", 0);
	Параметры.Вставить("ИмяОбработчика", "");
	Параметры.Вставить("ПовторнаяРегистрация", Ложь);
	Параметры.Вставить("ПараметрыВыборки");
	Параметры.Вставить("АктуальныеДанные", ПараметрыВыборкиАктуальныхДанных());
	Параметры.Вставить("ЗарегистрированныеТаблицыРегистраторов", Новый Соответствие);
	Параметры.Вставить("ВерсияПодсистемыНаНачалоОбновления", Неопределено);
	
	Если ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.ОбменДанными") Тогда
		
		Параметры.Вставить("ИмяФайлаСИзменениями", Неопределено);
		Параметры.Вставить("ЗаписьИзмененийДляПодчиненногоУзлаРИБСФильтрами", Неопределено);
		
	КонецЕсли;
	
	Возврат Параметры; 
	
КонецФункции

// Возвращает информацию о переданных данных в нормализованном виде. 
// Для использования в процедурах проверки блокировки данных отложенных обработчиков обновления.
//
// Параметры:
//  Данные  - СправочникОбъект
//          - ДокументОбъект
//          - ПланВидовХарактеристикОбъект
//          - ПланСчетовОбъект
//          - ПланВидовРасчетаОбъект
//          - РегистрСведенийНаборЗаписей
//          - РегистрНакопленияНаборЗаписей
//          - РегистрБухгалтерииНаборЗаписей
//          - РегистрРасчетаНаборЗаписей
//          - ЛюбаяСсылка
//          - ДанныеФормыСтруктура - значение входящего параметра Данные.
//  ДополнительныеПараметры - см. ОбновлениеИнформационнойБазы.ДополнительныеПараметрыОтметкиОбработки
// 
// Возвращаемое значение:
//  Структура:
//    * Данные - СправочникОбъект
//             - ДокументОбъект
//             - ПланВидовХарактеристикОбъект
//             - ПланСчетовОбъект
//             - ПланВидовРасчетаОбъект
//             - РегистрСведенийНаборЗаписей
//             - РегистрНакопленияНаборЗаписей
//             - РегистрБухгалтерииНаборЗаписей
//             - РегистрРасчетаНаборЗаписей
//             - ЛюбаяСсылка
//             - ДанныеФормыСтруктура - данные, которые нужно проанализировать. 
//    * МетаданныеОбъекта   - ОбъектМетаданных - объект метаданных, соответствующий параметру Данные.
//    * ПолноеИмя           - Строка      - полное имя объекта метаданных (см. метод ОбъектМетаданных.ПолноеИмя).
//    * Отбор               - ЛюбаяСсылка - если Данные - это ссылочный объект, то значение ссылки, 
//                                            если регистр подчиненный регистратору, - значение отбора по регистратору.
//			   	              - Структура   - если Данные - это независимый регистр сведений, то структура, соответствующая 
//                                            установленным отборам по измерениям.
//    * ЭтоНовый            - Булево      - если Данные - это ссылочный объект, то признак нового объекта. 
//                                            Для других типов - всегда Ложь.
//	
Функция МетаданныеИОтборПоДанным(Данные, ДополнительныеПараметры = Неопределено) Экспорт
	
	Если ДополнительныеПараметры = Неопределено Тогда
		ДополнительныеПараметры = ДополнительныеПараметрыОтметкиОбработки();
	КонецЕсли;
	
	Если ДополнительныеПараметры.ЭтоДвижения Тогда
		МетаданныеОбъекта = ОбщегоНазначения.ОбъектМетаданныхПоПолномуИмени(ДополнительныеПараметры.ПолноеИмяРегистра);		
	Иначе
		МетаданныеОбъекта = Неопределено;
	КонецЕсли;
	
	Отбор = Неопределено;
	ТипДанных = ТипЗнч(Данные);
	ЭтоНовый = Ложь;
	
	Если ТипЗнч(Данные) = Тип("Строка") Тогда
		МетаданныеОбъекта = ОбщегоНазначения.ОбъектМетаданныхПоПолномуИмени(Данные);
	ИначеЕсли ТипДанных = Тип("ДанныеФормыСтруктура") Тогда
		
		Если ОбщегоНазначенияКлиентСервер.ЕстьРеквизитИлиСвойствоОбъекта(Данные, "Ссылка") Тогда
			
			Если МетаданныеОбъекта = Неопределено Тогда
				МетаданныеОбъекта = Данные.Ссылка.Метаданные();
			КонецЕсли;
			
			Отбор = Данные.Ссылка;
			
			Если Не ЗначениеЗаполнено(Отбор) Тогда
				ЭтоНовый = Истина;
			КонецЕсли;
			
		ИначеЕсли ОбщегоНазначенияКлиентСервер.ЕстьРеквизитИлиСвойствоОбъекта(Данные, "ИсходныйКлючЗаписи") Тогда	

			Если МетаданныеОбъекта = Неопределено Тогда
				МетаданныеОбъекта = Метаданные.НайтиПоТипу(ТипЗнч(Данные.ИсходныйКлючЗаписи)); // ОбъектМетаданныхРегистрСведений 
			КонецЕсли;
			Отбор = Новый Структура;
			Для Каждого Измерение Из МетаданныеОбъекта.Измерения Цикл
				Отбор.Вставить(Измерение.Имя, Данные[Измерение.Имя]);
			КонецЦикла;
			
		Иначе
			ТекстИсключения = НСтр("ru = 'Процедура %1 не может быть использована для этой формы.'");
			ТекстИсключения = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(ТекстИсключения, "ОбновлениеИнформационнойБазы.МетаданныеИОтборПоДанным");
		КонецЕсли;
		
	Иначе
		
		Если МетаданныеОбъекта = Неопределено Тогда
			МетаданныеОбъекта = Данные.Метаданные();
		КонецЕсли;
		
		Если ОбщегоНазначения.ЭтоОбъектСсылочногоТипа(МетаданныеОбъекта) Тогда
			
			Если ОбщегоНазначения.ЭтоСсылка(ТипДанных) Тогда
				Отбор = Данные;
			Иначе
				Отбор = Данные.Ссылка;
				
				Если Данные.ЭтоНовый() Тогда
					ЭтоНовый = Истина;
				КонецЕсли;
			
			КонецЕсли;
			
		ИначеЕсли ОбщегоНазначения.ЭтоРегистрСведений(МетаданныеОбъекта)
			И МетаданныеОбъекта.РежимЗаписи = Метаданные.СвойстваОбъектов.РежимЗаписиРегистра.Независимый Тогда
			
			Отбор = Новый Структура;
			Для Каждого ЭлементОтбора Из Данные.Отбор Цикл
				Если ЭлементОтбора.Использование Тогда 
					Отбор.Вставить(ЭлементОтбора.Имя, ЭлементОтбора.Значение);
				КонецЕсли;
			КонецЦикла;
			
		ИначеЕсли ОбщегоНазначения.ЭтоРегистр(МетаданныеОбъекта) Тогда
			Если ДополнительныеПараметры.ЭтоДвижения Тогда
				Отбор = Данные;
			Иначе
				Отбор = Данные.Отбор.Регистратор.Значение;
			КонецЕсли;
		Иначе
			ТекстИсключения = НСтр("ru = 'Для этого типа метаданных не поддерживается анализ в функции %1.'");
			ТекстИсключения = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(ТекстИсключения, "ОбновлениеИнформационнойБазы.МетаданныеИОтборПоДанным");
			ВызватьИсключение ТекстИсключения;
		КонецЕсли;
		
	КонецЕсли;
	
	Результат = Новый Структура;
	Результат.Вставить("Данные", Данные);
	Результат.Вставить("Метаданные", МетаданныеОбъекта);
	Результат.Вставить("ПолноеИмя", МетаданныеОбъекта.ПолноеИмя());
	Результат.Вставить("Отбор", Отбор);
	Результат.Вставить("ЭтоНовый", ЭтоНовый);
	
	Возврат Результат;
КонецФункции

// Отмечает, что переданные данные необходимо обновить.
// Важно: не рекомендуется передавать в параметр Данные сразу все данные, которые
// необходимо зарегистрировать к обработке, т.к. большие коллекции типа Массив
// или ТаблицаЗначений могут занять существенный объем памяти сервера и привести
// к сильному снижению производительности системы. Рекомендуется получать и передавать
// данные небольшими порциями, например по 1000 объектов.
//
// Параметры:
//  ОсновныеПараметры - см. ОбновлениеИнформационнойБазы.ОсновныеПараметрыОтметкиКОбработке.
//  Данные            - ЛюбаяСсылка
//                    - Массив
//                    - РегистрСведенийНаборЗаписей, РегистрНакопленияНаборЗаписей, РегистрБухгалтерииНаборЗаписей
//                    - РегистрРасчетаНаборЗаписей - данные, по которым нужно зарегистрировать изменения.
//                    - ТаблицаЗначений - значения измерений независимого регистра сведений. Требования:
//                        а) нет измерений с именем "Узел";
//                        б) все измерения регистра должны входить в основной отбор;
//                        в) в таблице должны быть только колонки, соответствующие по именам измерениям регистра,
//                          по которым нужно регистрировать необходимость обработки;
//                        г) запись наборов в процессе обновления должна проходить с тем же отбором,
//                          что и регистрация необходимости обработки;
//                        д) в ДополнительныеПараметры нужно передать соответствующий признак и полное имя регистра.
//  ДополнительныеПараметры - см. ОбновлениеИнформационнойБазы.ДополнительныеПараметрыОтметкиОбработки.
// 
Процедура ОтметитьКОбработке(ОсновныеПараметры, Данные, ДополнительныеПараметры = Неопределено) Экспорт
	
	Если ДополнительныеПараметры = Неопределено Тогда
		ДополнительныеПараметры = ДополнительныеПараметрыОтметкиОбработки();
	КонецЕсли;
	
	Если (ТипЗнч(Данные) = Тип("Массив")
		Или ТипЗнч(Данные) = Тип("ТаблицаЗначений"))
		И Данные.Количество() = 0 Тогда
		Возврат;
	КонецЕсли;
	
	Если ОсновныеПараметры.Свойство("ПараметрыВыборки")
		И ТипЗнч(ОсновныеПараметры.ПараметрыВыборки) = Тип("Структура") Тогда
		ПолныеИменаОбъектов  = Неопределено;
		ПолныеИменаРегистров = Неопределено;
		ОсновныеПараметры.ПараметрыВыборки.Свойство("ПолныеИменаОбъектов", ПолныеИменаОбъектов);
		ОсновныеПараметры.ПараметрыВыборки.Свойство("ПолныеИменаРегистров", ПолныеИменаРегистров);
		
		МассивИмен = СтрРазделить(ПолныеИменаОбъектов, ",", Ложь);
		ОбщегоНазначенияКлиентСервер.ДополнитьМассив(МассивИмен, СтрРазделить(ПолныеИменаРегистров, ",", Ложь));
		
		Несуществующие = Новый Массив;
		Для Каждого ПолноеИмяОбъекта Из МассивИмен Цикл
			ПолноеИмяОбъекта = СокрЛП(ПолноеИмяОбъекта);
			ОбъектСуществует = (ОбщегоНазначения.ОбъектМетаданныхПоПолномуИмени(ПолноеИмяОбъекта) <> Неопределено);
			Если Не ОбъектСуществует Тогда
				Несуществующие.Добавить(ПолноеИмяОбъекта);
			КонецЕсли;
		КонецЦикла;
		
		Если Несуществующие.Количество() <> 0 Тогда
			ТекстИсключения = НСтр("ru = 'В свойстве %1 процедуры заполнения данных отложенного обработчика указаны несуществующие объекты:
				|%2.'");
			ТекстИсключения = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(ТекстИсключения,
				"ПараметрыВыборки", СтрСоединить(Несуществующие, ", "));
			ВызватьИсключение ТекстИсключения;
		КонецЕсли;
		
	КонецЕсли;
	
	Если ОсновныеПараметры.Свойство("ПерезапускОбновления")
		И ОсновныеПараметры.ПерезапускОбновления = Истина Тогда
		Узел = ОчередьСсылкой(ОсновныеПараметры.Очередь, Истина);
	Иначе
		Узел = ОчередьСсылкой(ОсновныеПараметры.Очередь);
	КонецЕсли;
	
	Если ДополнительныеПараметры.ЭтоДвижения Тогда
		
		ПолноеИмяРегистра = ДополнительныеПараметры.ПолноеИмяРегистра;
		
		Если ДополнительныеПараметры.ОтметитьВсеРегистраторы Тогда
			
			Если ТипЗнч(Данные) = Тип("ОбъектМетаданных") Тогда
				МетаданныеДокумента = Данные;
			ИначеЕсли ОбщегоНазначения.ЭтоСсылка(ТипЗнч(Данные)) Тогда
				МетаданныеДокумента = Данные.Метаданные();
			Иначе
				ТекстИсключения = НСтр("ru = 'Для регистрации всех регистраторов регистра необходимо в параметре ""Данные"" передать объект метаданных документ или ссылку на документ.'");
				ВызватьИсключение ТекстИсключения;
			КонецЕсли;
			ПолноеИмяДокумента = МетаданныеДокумента.ПолноеИмя();
			
			ТекстЗапроса =
			"ВЫБРАТЬ
			|	ТаблицаДокумента.Ссылка КАК Ссылка
			|ИЗ
			|	#ТаблицаДокумента КАК ТаблицаДокумента
			|ГДЕ
			|	ТаблицаДокумента.Проведен";
			
			ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "#ТаблицаДокумента", ПолноеИмяДокумента);
			Запрос = Новый Запрос;
			Запрос.Текст = ТекстЗапроса;
			
			Регистраторы = Запрос.Выполнить().Выгрузить().ВыгрузитьКолонку("Ссылка");
			
		Иначе
			Регистраторы = Данные;
			
			// Сохранение списка таблиц, по которым выполнялась регистрация.
			Для Каждого Регистратор Из Регистраторы Цикл
				ТипТаблицы = ТипЗнч(Регистратор);
				Если Регистратор.Пустая() Тогда
					Продолжить;
				КонецЕсли;
				
				Если ОсновныеПараметры.ЗарегистрированныеТаблицыРегистраторов[ТипТаблицы] = Неопределено Тогда
					ПолноеИмяТаблицы = Регистратор.Метаданные().ПолноеИмя();
					ОсновныеПараметры.ЗарегистрированныеТаблицыРегистраторов.Вставить(ТипТаблицы, ПолноеИмяТаблицы);
				КонецЕсли;
			КонецЦикла;
		КонецЕсли;
		
		ЗарегистрироватьИзмененияПодчиненногоРегистра(ОсновныеПараметры,
			Узел,
			Регистраторы,
			"ПодчиненныйРегистр",
			ПолноеИмяРегистра);
		
	ИначеЕсли ДополнительныеПараметры.ЭтоНезависимыйРегистрСведений Тогда
		
		ЗарегистрироватьИзмененияНезависимогоРегистра(ОсновныеПараметры,
			Узел,
			Данные,
			"НезависимыйРегистр",
			ДополнительныеПараметры.ПолноеИмяРегистра);
		
	Иначе
		Если ТипЗнч(Данные) = Тип("Массив") Или ОбщегоНазначения.ЭтоСсылка(ТипЗнч(Данные)) Тогда
			ЗарегистрироватьИзмененияОбъекта(ОсновныеПараметры, Узел, Данные, "Ссылка");
		Иначе
			Если ТипЗнч(Данные) = Тип("ОбъектМетаданных") Тогда
				ТекстИсключения = НСтр("ru = 'Не поддерживается регистрация к обновлению целиком объекта метаданных. Нужно обновлять конкретные данные.'");
				ВызватьИсключение ТекстИсключения;
			КонецЕсли;
			
			МетаданныеОбъекта = Метаданные.НайтиПоТипу(ТипЗнч(Данные));
			
			Если ОбщегоНазначения.ЭтоРегистрСведений(МетаданныеОбъекта)
				И МетаданныеОбъекта.РежимЗаписи = Метаданные.СвойстваОбъектов.РежимЗаписиРегистра.Независимый Тогда
				
				УстановитьНедостающиеОтборыВНаборе(Данные, МетаданныеОбъекта, Данные.Отбор);
				
			КонецЕсли;
			ЗарегистрироватьИзменения(ОсновныеПараметры, Узел, Данные, "НезависимыйРегистр", МетаданныеОбъекта.ПолноеИмя());
		КонецЕсли;
	КонецЕсли;
	
КонецПроцедуры

// Отмечает, что по переданным регистраторам нужно переформировать движения.
// 
// Параметры:
//  Параметры         - см. ОбновлениеИнформационнойБазы.ОсновныеПараметрыОтметкиКОбработке.
//  Регистраторы      - Массив - массив ссылок регистраторов.
//  ПолноеИмяРегистра - Строка - полное имя регистра, для которого необходимо обновить движения.
//
Процедура ОтметитьРегистраторыКОбработке(Параметры, Регистраторы, ПолноеИмяРегистра) Экспорт
	
	ДополнительныеПараметры = ДополнительныеПараметрыОтметкиОбработки();
	ДополнительныеПараметры.ЭтоДвижения = Истина;
	ДополнительныеПараметры.ПолноеИмяРегистра = ПолноеИмяРегистра;
	ОтметитьКОбработке(Параметры, Регистраторы, ДополнительныеПараметры);
	
КонецПроцедуры

// Дополнительные параметры выборки данных для обработки.
// 
// Возвращаемое значение:
//  Структура:
//   * ВыбиратьПорциями - Булево - выбирать данные для обработки порциями.
//                        Если выбираются документы, то порция определяется с учетом упорядочивания по убыванию
//                        по дате документа. Если выбираются регистраторы регистра, то порция определяется с
//                        учетом упорядочивания по убыванию по дате регистратора, если передано полное имя документа.
//                        Если полное имя документа не передано - упорядочивание происходит по периоду регистра:
//                        а) берется максимальная дата по каждому регистратору;
//                        б) если по регистратору нет записей, он в топе.
//   * ИмяВременнойТаблицы - Строка - параметр актуален для методов, создающих временные таблицы. Если имя не задано
//                           (поведение по умолчанию), то временная таблица будет создана с именем, указанным
//                           в описании каждого метода.
//   * ДополнительныеИсточникиДанных - Соответствие из КлючИЗначение - параметр актуален для методов, выбирающих
//                                     регистраторы и ссылки для обработки. В ключах соответствия может быть только один
//                                     из следующих видов данных:
//                                     1. Пути к реквизитам шапки документа или реквизитам табличных частей, которые
//                                        участвуют в соединениях с другими таблицами (в т.ч. неявных соединениях при
//                                        обращении "через точку").
//                                     2. Имена ссылочных объектов метаданных (Строка), в значениях которых находится
//                                        соответствие, в котором ключ - это имя регистра (Строка), а в значении
//                                        соответствие в ключах которого то же, что и в п. 1, т.е.
//                                        иерархия соответствий "Объект" -> "Регистр" -> "Источники".
//                                     Процедуры проверяют блокировку данных этих таблиц обработчиками меньших
//                                     очередей. Формат имен источников: <ИмяРеквизита> или
//                                     <ИмяТабличной>.<ИмяРеквизитаТабличнойЧасти>. Для удобства заполнения
//                                     см. УстановитьИсточникДанных().
//   * ПоляУпорядочивания  - Массив - имена полей независимого регистра сведений, используется для упорядочивания
//                                    результата запроса.
//   * МаксимумВыборки - Число - максимальное количество выбираемых записей.
//   * ИмяИзмеренияДляОтбора - Строка - имя измерения независимого регистра сведений, которому подчинены записи набора,
//                                      (аналог регистратора для регистров, подчиненных регистраторам).
//
Функция ДополнительныеПараметрыВыборкиДанныхДляОбработки() Экспорт
	
	ДополнительныеПараметры = Новый Структура;
	ДополнительныеПараметры.Вставить("ВыбиратьПорциями", Истина);
	ДополнительныеПараметры.Вставить("ИмяВременнойТаблицы", "");
	ДополнительныеПараметры.Вставить("ДополнительныеИсточникиДанных", Новый Соответствие);
	ДополнительныеПараметры.Вставить("ПоляУпорядочивания", Новый Массив);
	ДополнительныеПараметры.Вставить("МаксимумВыборки", МаксимальноеКоличествоЗаписейВВыборке());
	ДополнительныеПараметры.Вставить("ИмяИзмеренияДляОтбора", "Регистратор");
	
	Возврат ДополнительныеПараметры;
	
КонецФункции

// Параметры, используемые для выборки актуальных данных обработчика при выполнении обновления.
// Используется для обработчиков с обычным порядком. Данные, которые не удовлетворяют условию
// будут обрабатываться в порядке некритичных обработчиков.
// 
// Возвращаемое значение:
//   Структура:
//      * ПолеОтбора   - Строка - имя реквизита, по которому устанавливается отбор.
//      * ВидСравнения - ВидСравнения - допустимые значения - Больше, Меньше, Равно,
//                                      БольшеИлиРавно, МеньшеИлиРавно.
//      * Значение     - Произвольный - значение, по которому будет выполняться сравнение.
//
Функция ПараметрыВыборкиАктуальныхДанных() Экспорт
	
	Параметры = Новый Структура;
	Параметры.Вставить("ПолеОтбора");
	Параметры.Вставить("ВидСравнения");
	Параметры.Вставить("Значение");
	
	Возврат Параметры;
	
КонецФункции

// Дополнительные параметры выборки данных для многопоточной обработки.
//
// Возвращаемое значение:
//  Структура - поля из ДополнительныеПараметрыВыборкиДанныхДляОбработки(), дополненные следующими полями:
//   * ПолныеИменаОбъектов - Строка - полные имена обновляемых объектов (например, документов), разделенные запятыми.
//   * ПолныеИменаРегистров - Строка - полные имена регистров, разделенные запятыми.
//   * ПоляУпорядочиванияПриРаботеПользователей - Массив - поля упорядочивания, используемые при обновлении
//                                                с приоритетом работы пользователей.
//   * ПоляУпорядочиванияПриОбработкеДанных - Массив - поля упорядочивания, используемые при обновлении
//                                            с приоритетом обработки данных.
//   * СпособВыборки - Строка - один из способов выборки:
//                              ОбновлениеИнформационнойБазы.СпособВыборкиИзмеренияНезависимогоРегистраСведений(),
//                              ОбновлениеИнформационнойБазы.СпособВыборкиРегистраторыРегистра(),
//                              ОбновлениеИнформационнойБазы.СпособВыборкиСсылки().
//   * ПоследняяВыбраннаяЗапись - СписокЗначений - конец предыдущей выборки (служебное поле).
//   * ПерваяЗапись - СписокЗначений - начало выборки (служебное поле).
//   * ПоследняяЗапись - СписокЗначений - конец выборки (служебное поле).
//   * ОптимизироватьВыборкуПоСтраницам - Булево - если Истина, то выборка выполняется без ИЛИ, значение Ложь может
//                                        быть полезно, если исходный запрос не оптимален, тогда с ИЛИ будет быстрее.
//
Функция ДополнительныеПараметрыВыборкиДанныхДляМногопоточнойОбработки() Экспорт
	
	ДополнительныеПараметры = ДополнительныеПараметрыВыборкиДанныхДляОбработки();
	ДополнительныеПараметры.Вставить("ПолныеИменаОбъектов");
	ДополнительныеПараметры.Вставить("ПолныеИменаРегистров");
	ДополнительныеПараметры.Вставить("ПоляУпорядочиванияПриРаботеПользователей", Новый Массив);
	ДополнительныеПараметры.Вставить("ПоляУпорядочиванияПриОбработкеДанных", Новый Массив);
	ДополнительныеПараметры.Вставить("СпособВыборки");
	ДополнительныеПараметры.Вставить("ПоследняяВыбраннаяЗапись");
	ДополнительныеПараметры.Вставить("ПерваяЗапись");
	ДополнительныеПараметры.Вставить("ПоследняяЗапись");
	ДополнительныеПараметры.Вставить("ОптимизироватьВыборкуПоСтраницам", Истина);
	
	Возврат ДополнительныеПараметры;
	
КонецФункции

// Установить параметр ДополнительныеИсточникиДанных в структуре, возвращаемой функцией
// ДополнительныеПараметрыВыборкиДанныхДляОбработки().
//
// Используется, когда источники данных нужно установить в разрезе документов и регистров.
// Применяется при многопоточном обновлении.
//
// Параметры:
//  ДополнительныеИсточникиДанных - см. ДополнительныеПараметрыВыборкиДанныхДляОбработки
//  Источник - см. ДополнительныеПараметрыВыборкиДанныхДляОбработки
//  Объект - Строка - имя документа (полное или короткое).
//  Регистр - Строка - имя регистра (полное или короткое).
//
Процедура УстановитьИсточникДанных(ДополнительныеИсточникиДанных, Источник, Объект = "", Регистр = "") Экспорт
	
	ИмяОбъекта = ИмяОбъектаМетаданных(Объект);
	ИмяРегистра = ИмяОбъектаМетаданных(Регистр);
	
	Если ПустаяСтрока(ИмяОбъекта) И ПустаяСтрока(ИмяРегистра) Тогда
		ДополнительныеИсточникиДанных.Вставить(Источник);
	Иначе
		РегистрыОбъекта = ДополнительныеИсточникиДанных[ИмяОбъекта];
		
		Если РегистрыОбъекта = Неопределено Тогда
			РегистрыОбъекта = Новый Соответствие;
			ДополнительныеИсточникиДанных[ИмяОбъекта] = РегистрыОбъекта;
		КонецЕсли;
		
		ИсточникиДанных = РегистрыОбъекта[ИмяРегистра];
		
		Если ИсточникиДанных = Неопределено Тогда
			ИсточникиДанных = Новый Соответствие;
			РегистрыОбъекта[ИмяРегистра] = ИсточникиДанных;
		КонецЕсли;
		
		ИсточникиДанных.Вставить(Источник);
	КонецЕсли;
	
КонецПроцедуры

// Получить значение параметра ДополнительныеИсточникиДанных из структуры, возвращаемой
// функцией ДополнительныеПараметрыВыборкиДанныхДляОбработки().
//
// Можно использовать, когда источники данных нужно получить в разрезе документов и регистров.
// Применяется при многопоточном обновлении.
//
// Параметры:
//  ДополнительныеИсточникиДанных - см. ДополнительныеПараметрыВыборкиДанныхДляОбработки
//  Объект - Строка - имя документа (полное или короткое).
//  Регистр - Строка - имя регистра (полное или короткое).
//
// Возвращаемое значение:
//  Соответствие - источники данных для указанного документа и регистра.
//
Функция ИсточникиДанных(ДополнительныеИсточникиДанных, Объект = "", Регистр = "") Экспорт
	
	Если ЭтоПростойИсточникДанных(ДополнительныеИсточникиДанных) Тогда
		Возврат ДополнительныеИсточникиДанных;
	Иначе
		ИмяОбъекта = ИмяОбъектаМетаданных(Объект);
		ИмяРегистра = ИмяОбъектаМетаданных(Регистр);
		РегистрыОбъекта = ДополнительныеИсточникиДанных[ИмяОбъекта];
		ТипСоответствие = Тип("Соответствие");
		
		Если ТипЗнч(РегистрыОбъекта) = ТипСоответствие Тогда
			ИсточникиДанных = РегистрыОбъекта[ИмяРегистра];
			
			Если ТипЗнч(ИсточникиДанных) = ТипСоответствие Тогда
				Возврат ИсточникиДанных;
			КонецЕсли;
		КонецЕсли;
		
		Возврат Новый Соответствие;
	КонецЕсли;
	
КонецФункции

// Создает временную таблицу ссылок, которые не обработаны в текущей очереди
// и не заблокированы меньшими очередями.
// Имя таблицы: ВТДляОбработки<ИмяРегистра>, например ВТДляОбработкиТоварыНаСкладах.
// Колонки таблицы:
//   Регистратор - ДокументСсылка.
//
// Параметры:
//  Очередь					 - Число  - очередь обработки, в которой выполняется текущий обработчик.
//  ПолноеИмяДокумента		 - Строка - имя документа, движения по которому нужно переформировать. 
//                             Если движения формируются не по данным документа, то нужно передать Неопределено - 
//                             тогда не будет проверяться блокировка таблицы документа.
//                             Например, "Документ.ПриходныйОрдерНаТовары".
//  ПолноеИмяРегистра	 - Строка	 - имя регистра, движения по которому нужно переформировать.
//  	                   Например, "РегистрНакопления.ТоварыНаСкладах".
//  МенеджерВременныхТаблиц	 - МенеджерВременныхТаблиц - менеджер, в котором будет создана временная таблица.
//  ДополнительныеПараметры	 - см. ОбновлениеИнформационнойБазы.ДополнительныеПараметрыВыборкиДанныхДляОбработки.
// 
// Возвращаемое значение:
//  Структура - результат формирования временной таблицы:
//   * ЕстьЗаписиВоВременнойТаблице - Булево - в создаваемой таблице есть хотя бы одна запись. 
//                                             Записей может не быть по двум причинам:
//                                             все обработано или все, что нужно обработать, еще заблокировано 
//                                             обработчиками с меньшей очередью.
//   * ЕстьДанныеДляОбработки - Булево - в очереди есть ссылки для обработки, т.е. еще не все обработано.
//   * ИмяВременнойТаблицы - Строка - имя созданной временной таблицы.
//
Функция СоздатьВременнуюТаблицуРегистраторовРегистраДляОбработки(Очередь, ПолноеИмяДокумента, ПолноеИмяРегистра, МенеджерВременныхТаблиц, ДополнительныеПараметры = Неопределено) Экспорт
	
	Если ДополнительныеПараметры = Неопределено Тогда
		ДополнительныеПараметры = ДополнительныеПараметрыВыборкиДанныхДляОбработки();
	КонецЕсли;
	
	ИмяРегистра = СтрРазделить(ПолноеИмяРегистра,".",Ложь)[1];
	
	ТекущаяОчередь = ОчередьСсылкой(Очередь);
	
	Если ПолноеИмяДокумента = Неопределено Тогда 
		Если ДополнительныеПараметры.ВыбиратьПорциями Тогда
			ТекстЗапроса =
			"ВЫБРАТЬ ПЕРВЫЕ 1000
			|	ТаблицаРегистраИзменения.Регистратор КАК Регистратор,
			|	МАКСИМУМ(ЕСТЬNULL(ТаблицаРегистра.Период, ДАТАВРЕМЯ(3000, 1, 1))) КАК Период
			|ПОМЕСТИТЬ #ВТДляОбработкиРегистратор
			|ИЗ
			|	#ТаблицаРегистраИзменения КАК ТаблицаРегистраИзменения
			|		ЛЕВОЕ СОЕДИНЕНИЕ #ТаблицаДвиженийРегистра КАК ТаблицаРегистра
			|		ПО ТаблицаРегистраИзменения.Регистратор = ТаблицаРегистра.Регистратор
			|ГДЕ
			|	ТаблицаРегистраИзменения.Узел = &ТекущаяОчередь
			|	И НЕ ИСТИНА В (
			|		ВЫБРАТЬ ПЕРВЫЕ 1
			|			ИСТИНА
			|		ИЗ
			|			ВТЗаблокированоРегистратор КАК ВТЗаблокированоРегистратор
			|		ГДЕ
			|			ТаблицаРегистраИзменения.Регистратор = ВТЗаблокированоРегистратор.Регистратор)
			|	И &УсловиеПоДопИсточникамРегистрам
			|
			|СГРУППИРОВАТЬ ПО
			|	ТаблицаРегистраИзменения.Регистратор
			|
			|УПОРЯДОЧИТЬ ПО
			|	МАКСИМУМ(ЕСТЬNULL(ТаблицаРегистра.Период, ДАТАВРЕМЯ(3000, 1, 1)))
			|
			|ИНДЕКСИРОВАТЬ ПО
			|	Регистратор
			|
			|;
			|
			|////////////////////////////////////////////////////////////////////////////////
			|УНИЧТОЖИТЬ ВТЗаблокированоРегистратор"; // @query-part
			
			ТекстЗапроса = СтрЗаменить(ТекстЗапроса,"#ТаблицаДвиженийРегистра", ПолноеИмяРегистра);	
		Иначе
			ТекстЗапроса =
			"ВЫБРАТЬ
			|	ТаблицаРегистраИзменения.Регистратор КАК Регистратор
			|ПОМЕСТИТЬ #ВТДляОбработкиРегистратор
			|ИЗ
			|	#ТаблицаРегистраИзменения КАК ТаблицаРегистраИзменения
			|ГДЕ
			|	ТаблицаРегистраИзменения.Узел = &ТекущаяОчередь
			|	И НЕ ИСТИНА В (
			|		ВЫБРАТЬ ПЕРВЫЕ 1
			|			ИСТИНА
			|		ИЗ
			|			ВТЗаблокированоРегистратор КАК ВТЗаблокированоРегистратор
			|		ГДЕ
			|			ТаблицаРегистраИзменения.Регистратор = ВТЗаблокированоРегистратор.Регистратор)
			|	И &УсловиеПоДопИсточникамРегистрам
			|
			|ИНДЕКСИРОВАТЬ ПО
			|	Регистратор
			|;
			|
			|////////////////////////////////////////////////////////////////////////////////
			|УНИЧТОЖИТЬ ВТЗаблокированоРегистратор";
		КонецЕсли;
	Иначе
		ФрагментыЗапроса = Новый Массив;
		ФрагментЗапроса =
			"ВЫБРАТЬ ПЕРВЫЕ 1000
			|	ТаблицаДокумента.Ссылка КАК Регистратор
			|ПОМЕСТИТЬ #ВТДляОбработкиРегистратор
			|ИЗ
			|	#ТаблицаРегистраИзменения КАК ТаблицаРегистраИзменения
			|		ВНУТРЕННЕЕ СОЕДИНЕНИЕ #ПолноеИмяДокумента КАК ТаблицаДокумента
			|		ПО ТаблицаРегистраИзменения.Регистратор = ТаблицаДокумента.Ссылка
			|ГДЕ
			|	ТаблицаРегистраИзменения.Узел = &ТекущаяОчередь
			|	И НЕ ИСТИНА В (
			|		ВЫБРАТЬ ПЕРВЫЕ 1
			|			ИСТИНА
			|		ИЗ
			|			ВТЗаблокированоРегистратор КАК ВТЗаблокированоРегистратор
			|		ГДЕ
			|			ТаблицаДокумента.Ссылка = ВТЗаблокированоРегистратор.Регистратор)
			|	И НЕ ИСТИНА В (
			|		ВЫБРАТЬ ПЕРВЫЕ 1
			|			ИСТИНА
			|		ИЗ
			|			ВТЗаблокированоСсылка КАК ВТЗаблокированоСсылка
			|		ГДЕ
			|			ТаблицаДокумента.Ссылка = ВТЗаблокированоСсылка.Ссылка)
			|	И &УсловиеПоДопИсточникамСсылкам
			|	И &УсловиеПоДопИсточникамРегистрам";
		ФрагментыЗапроса.Добавить(ФрагментЗапроса);
		
		Если ДополнительныеПараметры.ВыбиратьПорциями Тогда
			ФрагментЗапроса =
				"
				|УПОРЯДОЧИТЬ ПО
				|	ТаблицаДокумента.Дата УБЫВ";
			ФрагментыЗапроса.Добавить(ФрагментЗапроса);
		КонецЕсли;
		
		ФрагментЗапроса =
			"
			|ИНДЕКСИРОВАТЬ ПО
			|	Регистратор
			|;
			|
			|////////////////////////////////////////////////////////////////////////////////
			|УНИЧТОЖИТЬ ВТЗаблокированоРегистратор
			|;
			|
			|////////////////////////////////////////////////////////////////////////////////
			|УНИЧТОЖИТЬ ВТЗаблокированоСсылка";
		ФрагментыЗапроса.Добавить(ФрагментЗапроса);
		ТекстЗапроса = СтрСоединить(ФрагментыЗапроса, Символы.ПС);
		
		ДополнительныеПараметрыСозданияВТ = ДополнительныеПараметрыВыборкиДанныхДляОбработки();
		ДополнительныеПараметрыСозданияВТ.ИмяВременнойТаблицы = "ВТЗаблокированоСсылка";
		СоздатьВременнуюТаблицуЗаблокированныхДляЧтенияИИзмененияДанных(Очередь, ПолноеИмяДокумента, МенеджерВременныхТаблиц, ДополнительныеПараметрыСозданияВТ);
	КонецЕсли;
	
	Если ПустаяСтрока(ДополнительныеПараметры.ИмяВременнойТаблицы) Тогда
		ИмяВременнойТаблицы = "ВТДляОбработки" + ИмяРегистра;
	Иначе
		ИмяВременнойТаблицы = ДополнительныеПараметры.ИмяВременнойТаблицы;
	КонецЕсли;
	
	ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "#ТаблицаРегистраИзменения", ПолноеИмяРегистра + ".Изменения");	
	ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "#ВТДляОбработкиРегистратор", ИмяВременнойТаблицы);
	
	ДополнительныеПараметрыСозданияВТ = ДополнительныеПараметрыВыборкиДанныхДляОбработки();
	ДополнительныеПараметрыСозданияВТ.ИмяВременнойТаблицы = "ВТЗаблокированоРегистратор";
	СоздатьВременнуюТаблицуЗаблокированныхДляЧтенияИИзмененияДанных(Очередь, ПолноеИмяРегистра, МенеджерВременныхТаблиц, ДополнительныеПараметрыСозданияВТ);
	
	ДобавитьПроверкуБлокировкиДополнительныхИсточников(Очередь, ТекстЗапроса, ПолноеИмяДокумента, ПолноеИмяРегистра, МенеджерВременныхТаблиц, Истина, ДополнительныеПараметры);
	
	ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "#ПолноеИмяДокумента", ПолноеИмяДокумента);
		
	Запрос = Новый Запрос;
	Запрос.Текст = ТекстЗапроса;
	Запрос.МенеджерВременныхТаблиц = МенеджерВременныхТаблиц;
	Запрос.УстановитьПараметр("ТекущаяОчередь", ТекущаяОчередь);
	РезультатЗапроса = Запрос.ВыполнитьПакет();
	
	Результат = Новый Структура("ЕстьЗаписиВоВременнойТаблице,ЕстьДанныеДляОбработки,ИмяВременнойТаблицы", Ложь, Ложь, "");
	Результат.ИмяВременнойТаблицы = ИмяВременнойТаблицы;
	Результат.ЕстьЗаписиВоВременнойТаблице = РезультатЗапроса[0].Выгрузить()[0].Количество <> 0;
	
	Если Результат.ЕстьЗаписиВоВременнойТаблице Тогда
		Результат.ЕстьДанныеДляОбработки = Истина;
	Иначе
		Результат.ЕстьДанныеДляОбработки = ЕстьДанныеДляОбработки(Очередь, ПолноеИмяРегистра);
	КонецЕсли;	
	
	Возврат Результат; 
	
КонецФункции

// Возвращает порцию регистраторов, по которым нужно переформировать движения.
//  Данные берутся из зарегистрированных в очереди, учитываются заблокированные более приоритетными очередями данные.
//  Блокировка по другим очередям делается по документу и по регистру.
//  Регистраторы в выборке упорядочены по дате регистратора по убыванию, если передано полное имя документа.
//  Если полное имя документа не передано - упорядочивание происходит по периоду регистра:
//				- берется максимальная дата по каждому регистратору;
//				- если по регистратору нет записей - он в топе.
// Параметры:
//  Очередь					 - Число - очередь, к которой отнесен обработчик и в которой зарегистрированы данные, которые он будет обрабатывать.
//  ПолноеИмяДокумента		 - Строка - имя документа, движения по которому нужно переформировать. Если движения формируются не по данным
//									документа, то нужно передать Неопределено - тогда не будет проверяться блокировка таблицы документа.
//									Например, Документ.ПриходныйОрдерНаТовары.
//  ПолноеИмяРегистра		 - Строка	 - имя регистра, движения по которому нужно переформировать.
//  	Например, РегистрНакопления.ТоварыНаСкладах.
//  ДополнительныеПараметры	 - см. ОбновлениеИнформационнойБазы.ДополнительныеПараметрыВыборкиДанныхДляОбработки.
// 
// Возвращаемое значение:
//   ВыборкаИзРезультатаЗапроса - выборка регистраторов, которые нужно обработать, поля выборки:
//     * Регистратор - ДокументСсылка.
//     * Период - Дата - дата документа, если передано полное имя документа, максимальный период по регистратору,
//                       если полное имя документа не передано.
//     * Проведен - Булево
//                - Неопределено - значение реквизита Проведен документа, если передано полное имя документа,
//                                 или Неопределено, если имя документа не передано.
//   ТаблицаЗначений - данные, которые нужно обработать, имена колонок соответствуют именам измерений регистра.
//
Функция ВыбратьРегистраторыРегистраДляОбработки(Очередь, ПолноеИмяДокумента, ПолноеИмяРегистра, ДополнительныеПараметры = Неопределено) Экспорт
	
	Если ДополнительныеПараметры = Неопределено Тогда
		ДополнительныеПараметры = ДополнительныеПараметрыВыборкиДанныхДляОбработки();
	КонецЕсли;
	
	ПараметрыОбработчикаОбновления = Неопределено;
	ДополнительныеПараметры.Свойство("ПараметрыОбработчикаОбновления", ПараметрыОбработчикаОбновления);
	
	МенеджерВременныхТаблиц = Новый МенеджерВременныхТаблиц();
	ПроверитьПараметрыВыборки(ДополнительныеПараметры);
	ПараметрыПостроения = ПараметрыПостроенияВыборки(ДополнительныеПараметры);
	
	ЕстьФильтр = Ложь;
	Если ПолноеИмяДокумента = Неопределено Тогда
		ТекстЗапроса =
		"ВЫБРАТЬ ПЕРВЫЕ 1000
		|	&ВыбираемыеПоля
		|ИЗ
		|	#ТаблицаРегистраИзменения КАК ТаблицаРегистраИзменения
		|		ЛЕВОЕ СОЕДИНЕНИЕ #ТаблицаДвиженийРегистра КАК ТаблицаРегистра
		|		ПО ТаблицаРегистраИзменения.Регистратор = ТаблицаРегистра.Регистратор
		|ГДЕ
		|	ТаблицаРегистраИзменения.Узел = &ТекущаяОчередь
		|	И НЕ ИСТИНА В (
		|		ВЫБРАТЬ ПЕРВЫЕ 1
		|			ИСТИНА
		|		ИЗ
		|			ВТЗаблокированоРегистратор КАК ВТЗаблокированоРегистратор
		|		ГДЕ
		|			ТаблицаРегистраИзменения.Регистратор = ВТЗаблокированоРегистратор.Регистратор)
		|	И &УсловиеПоДопИсточникамРегистрам
		|
		|СГРУППИРОВАТЬ ПО
		|	ТаблицаРегистраИзменения.Регистратор";
		
		Если ПараметрыПостроения.ПостраничнаяВыборка Тогда
			ТекстЗапроса = ТекстЗапроса + "
				|
				|ИМЕЮЩИЕ
				|	&УсловиеПоСтраницам"
		КонецЕсли;
		
		ТекстЗапроса = ТекстЗапроса + "
			|
			|УПОРЯДОЧИТЬ ПО
			|	&ПорядокВыборки";
		ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "#ТаблицаДвиженийРегистра", ПолноеИмяРегистра);
		УстановитьПоляУпорядочиванияРегистра(ПараметрыПостроения);
	Иначе
		ТекстЗапроса =
		"ВЫБРАТЬ ПЕРВЫЕ 1000
		|	&ВыбираемыеПоля
		|ИЗ
		|	#ТаблицаРегистраИзменения КАК ТаблицаРегистраИзменения
		|		ВНУТРЕННЕЕ СОЕДИНЕНИЕ #ПолноеИмяДокумента КАК ТаблицаДокумента
		|		ПО ТаблицаРегистраИзменения.Регистратор = ТаблицаДокумента.Ссылка
		|
		|ГДЕ
		|	ТаблицаРегистраИзменения.Узел = &ТекущаяОчередь
		|	И НЕ ИСТИНА В (
		|		ВЫБРАТЬ ПЕРВЫЕ 1
		|			ИСТИНА
		|		ИЗ
		|			ВТЗаблокированоРегистратор КАК ВТЗаблокированоРегистратор
		|		ГДЕ
		|			ТаблицаРегистраИзменения.Регистратор = ВТЗаблокированоРегистратор.Регистратор)
		|	И НЕ ИСТИНА В (
		|		ВЫБРАТЬ ПЕРВЫЕ 1
		|			ИСТИНА
		|		ИЗ
		|			ВТЗаблокированоСсылка КАК ВТЗаблокированоСсылка
		|		ГДЕ
		|			ТаблицаРегистраИзменения.Регистратор = ВТЗаблокированоСсылка.Ссылка)
		|	И &УсловиеПоДопИсточникамСсылкам
		|	И &УсловиеПоДопИсточникамРегистрам
		|	И &УсловиеПоСтраницам
		|	И &УсловиеАктуальныеДанные
		|
		|УПОРЯДОЧИТЬ ПО
		|	&ПорядокВыборки";
		ДополнительныеПараметрыСозданияВТ = ДополнительныеПараметрыВыборкиДанныхДляОбработки();
		ДополнительныеПараметрыСозданияВТ.ИмяВременнойТаблицы = "ВТЗаблокированоСсылка";
		СоздатьВременнуюТаблицуЗаблокированныхДляЧтенияИИзмененияДанных(Очередь, ПолноеИмяДокумента, МенеджерВременныхТаблиц, ДополнительныеПараметрыСозданияВТ);
		УстановитьПоляУпорядочиванияРегистраПоДокументу(ПараметрыПостроения);
		
		ФильтрАктуальныхДанных = ОбновлениеИнформационнойБазыСлужебный.ФильтрАктуальныхДанных(ПараметрыОбработчикаОбновления, "ТаблицаДокумента");
		ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&УсловиеАктуальныеДанные", ФильтрАктуальныхДанных.Условие);
		
		Если ФильтрАктуальныхДанных.ЕстьФильтр Тогда
			ЕстьФильтр = Истина;
			ТекстЗапросаПроверки =
				"ВЫБРАТЬ ПЕРВЫЕ 1
				|	ИСТИНА
				|ИЗ
				|	#ТаблицаРегистраИзменения КАК ТаблицаРегистраИзменения
				|		ВНУТРЕННЕЕ СОЕДИНЕНИЕ #ПолноеИмяДокумента КАК ТаблицаДокумента
				|		ПО ТаблицаРегистраИзменения.Регистратор = ТаблицаДокумента.Ссылка
				|ГДЕ
				|	ТаблицаРегистраИзменения.Узел = &ТекущаяОчередь
				|	И &УсловиеАктуальныеДанные";
			ТекстЗапросаПроверки = СтрЗаменить(ТекстЗапросаПроверки, "&УсловиеАктуальныеДанные", ФильтрАктуальныхДанных.Условие);
			ТекстЗапросаПроверки = СтрЗаменить(ТекстЗапросаПроверки,"#ТаблицаРегистраИзменения", ПолноеИмяРегистра + ".Изменения");
			ТекстЗапросаПроверки = СтрЗаменить(ТекстЗапросаПроверки,"#ПолноеИмяДокумента", ПолноеИмяДокумента);
			
			ЗапросПроверки = Новый Запрос;
			ЗапросПроверки.УстановитьПараметр("ТекущаяОчередь", ОчередьСсылкой(Очередь));
			ЗапросПроверки.УстановитьПараметр("ЗначениеОтбораАктуальныхДанных", ФильтрАктуальныхДанных.Значение);
			ЗапросПроверки.Текст = ТекстЗапросаПроверки;
			
			ОбработаныВсеАктуальныеДанные = ЗапросПроверки.Выполнить().Пустой();
			УстановитьПараметрыОбработчикаПриВыборкеДанных(ДополнительныеПараметры, ОбработаныВсеАктуальныеДанные, ПараметрыОбработчикаОбновления, ПолноеИмяДокумента);
		КонецЕсли;
	КонецЕсли;
	
	ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "#ТаблицаРегистраИзменения", ПолноеИмяРегистра + ".Изменения");	
	
	ДополнительныеПараметрыСозданияВТ = ДополнительныеПараметрыВыборкиДанныхДляОбработки();
	ДополнительныеПараметрыСозданияВТ.ИмяВременнойТаблицы = "ВТЗаблокированоРегистратор";
	ИмяИзмеренияДляОтбора = ДополнительныеПараметры.ИмяИзмеренияДляОтбора;
	
	Если ВРег(ИмяИзмеренияДляОтбора) <> ВРег("Регистратор") Тогда
		ДополнительныеПараметрыСозданияВТ.ИмяИзмеренияДляОтбора = ИмяИзмеренияДляОтбора;
		Измерение = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(".%1", ИмяИзмеренияДляОтбора);
		ТекстЗапроса = СтрЗаменить(ТекстЗапроса, ".Регистратор", Измерение);
	КонецЕсли;
	
	СоздатьВременнуюТаблицуЗаблокированныхДляЧтенияИИзмененияДанных(Очередь, ПолноеИмяРегистра, МенеджерВременныхТаблиц, ДополнительныеПараметрыСозданияВТ);
	УстановитьРазмерВыборки(ТекстЗапроса, ДополнительныеПараметры);
	ДобавитьПроверкуБлокировкиДополнительныхИсточников(Очередь, ТекстЗапроса, ПолноеИмяДокумента, ПолноеИмяРегистра, МенеджерВременныхТаблиц, Ложь, ДополнительныеПараметры);
	
	ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "#ПолноеИмяДокумента", ПолноеИмяДокумента);
		
	Запрос = Новый Запрос;
	Запрос.Текст = ТекстЗапроса;
	Запрос.МенеджерВременныхТаблиц = МенеджерВременныхТаблиц;
	Запрос.УстановитьПараметр("ТекущаяОчередь", ОчередьСсылкой(Очередь));
	Если ЕстьФильтр Тогда
		Запрос.УстановитьПараметр("ЗначениеОтбораАктуальныхДанных", ФильтрАктуальныхДанных.Значение);
	КонецЕсли;
	
	УстановитьПоляПоСтраницам(Запрос, ПараметрыПостроения);
	УстановитьПорядокПоСтраницам(Запрос, ПараметрыПостроения);
	
	Возврат ВыбратьДанныеДляОбработки(Запрос, ПараметрыПостроения);
	
КонецФункции

// Возвращает порцию ссылок, по которым нужно произвести обработку.
//  Данные берутся из зарегистрированных в очереди, учитываются заблокированные более приоритетными очередями данные.
//	Ссылки на документы возвращаются упорядоченными по убыванию по дате.
//
// Параметры:
//  Очередь				 - Число - очередь, к которой отнесен обработчик и в которой зарегистрированы данные, которые он будет
//									обрабатывать.
//  ПолноеИмяОбъекта	 - Строка	 - имя объекта, который нужно обработать. Например, Документ.ПриходныйОрдерНаТовары.
//  ДополнительныеПараметры	 - см. ОбновлениеИнформационнойБазы.ДополнительныеПараметрыВыборкиДанныхДляОбработки.
// 
// Возвращаемое значение:
//   ВыборкаИзРезультатаЗапроса - выборка ссылок, которые нужно обработать, поля выборки:
//     * Ссылка - ЛюбаяСсылка.
//   ТаблицаЗначений - данные, которые нужно обработать, имена колонок соответствуют именам измерений регистра.
//
Функция ВыбратьСсылкиДляОбработки(Очередь, ПолноеИмяОбъекта, ДополнительныеПараметры = Неопределено) Экспорт
	Если ДополнительныеПараметры = Неопределено Тогда
		ДополнительныеПараметры = ДополнительныеПараметрыВыборкиДанныхДляОбработки();
	КонецЕсли;
	
	ПараметрыОбработчикаОбновления = Неопределено;
	ДополнительныеПараметры.Свойство("ПараметрыОбработчикаОбновления", ПараметрыОбработчикаОбновления);
	
	ИмяОбъекта = СтрРазделить(ПолноеИмяОбъекта,".",Ложь)[1];
	МетаданныеОбъекта = ОбщегоНазначения.ОбъектМетаданныхПоПолномуИмени(ПолноеИмяОбъекта);
	ЭтоДокумент = ОбщегоНазначения.ЭтоДокумент(МетаданныеОбъекта)
				Или ОбщегоНазначения.ЭтоЗадача(МетаданныеОбъекта);
	
	ПроверитьПараметрыВыборки(ДополнительныеПараметры);
	ПараметрыПостроения = ПараметрыПостроенияВыборки(ДополнительныеПараметры);
	
	ТекстЗапроса =
	"ВЫБРАТЬ ПЕРВЫЕ 1000
	|	&ВыбираемыеПоля
	|ИЗ
	|	#ТаблицаОбъектаИзменения КАК ТаблицаИзменений
	|		ВНУТРЕННЕЕ СОЕДИНЕНИЕ #ТаблицаОбъекта КАК ТаблицаОбъекта
	|		ПО ТаблицаИзменений.Ссылка = ТаблицаОбъекта.Ссылка
	|ГДЕ
	|	ТаблицаИзменений.Узел = &ТекущаяОчередь
	|	И НЕ ИСТИНА В (
	|		ВЫБРАТЬ ПЕРВЫЕ 1
	|			ИСТИНА
	|		ИЗ
	|			#ВТЗаблокированоСсылка КАК ВТЗаблокированоСсылка
	|		ГДЕ
	|			ТаблицаИзменений.Ссылка = ВТЗаблокированоСсылка.Ссылка)
	|	И &УсловиеПоДопИсточникамСсылкам
	|	И &УсловиеПоДопИсточникамРегистрам
	|	И &УсловиеПоСтраницам
	|	И &УсловиеАктуальныеДанные";
	Если ЭтоДокумент Или ПараметрыПостроения.ПостраничнаяВыборка Тогда
		ТекстЗапроса = ТекстЗапроса + "
		|
		|УПОРЯДОЧИТЬ ПО
		|	&ПорядокВыборки";
	КонецЕсли;
	
	ФильтрАктуальныхДанных = ОбновлениеИнформационнойБазыСлужебный.ФильтрАктуальныхДанных(ПараметрыОбработчикаОбновления, "ТаблицаОбъекта");
	ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&УсловиеАктуальныеДанные", ФильтрАктуальныхДанных.Условие);
	Если ФильтрАктуальныхДанных.ЕстьФильтр Тогда
		ТекстЗапросаПроверки =
			"ВЫБРАТЬ ПЕРВЫЕ 1
			|	ИСТИНА
			|ИЗ
			|	#ТаблицаОбъектаИзменения КАК ТаблицаИзменений
			|		ВНУТРЕННЕЕ СОЕДИНЕНИЕ #ТаблицаОбъекта КАК ТаблицаОбъекта
			|		ПО ТаблицаИзменений.Ссылка = ТаблицаОбъекта.Ссылка
			|ГДЕ
			|	ТаблицаИзменений.Узел = &ТекущаяОчередь
			|	И &УсловиеАктуальныеДанные";
		ТекстЗапросаПроверки = СтрЗаменить(ТекстЗапросаПроверки, "&УсловиеАктуальныеДанные", ФильтрАктуальныхДанных.Условие);
		ТекстЗапросаПроверки = СтрЗаменить(ТекстЗапросаПроверки,"#ТаблицаОбъектаИзменения", ПолноеИмяОбъекта + ".Изменения");
		ТекстЗапросаПроверки = СтрЗаменить(ТекстЗапросаПроверки,"#ТаблицаОбъекта", ПолноеИмяОбъекта);
		
		ЗапросПроверки = Новый Запрос;
		ЗапросПроверки.УстановитьПараметр("ТекущаяОчередь", ОчередьСсылкой(Очередь));
		ЗапросПроверки.УстановитьПараметр("ЗначениеОтбораАктуальныхДанных", ФильтрАктуальныхДанных.Значение);
		ЗапросПроверки.Текст = ТекстЗапросаПроверки;
		
		ОбработаныВсеАктуальныеДанные = ЗапросПроверки.Выполнить().Пустой();
		
		УстановитьПараметрыОбработчикаПриВыборкеДанных(ДополнительныеПараметры, ОбработаныВсеАктуальныеДанные, ПараметрыОбработчикаОбновления)
	КонецЕсли;
	
	ТекстЗапроса = ТекстЗапроса + "
	|;
	|УНИЧТОЖИТЬ
	|	#ВТЗаблокированоСсылка"; 
	УстановитьПоляУпорядочиванияСсылок(ПараметрыПостроения, ЭтоДокумент);
	ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "#ВТЗаблокированоСсылка","ВТЗаблокировано" + ИмяОбъекта);
	ТекстЗапроса = СтрЗаменить(ТекстЗапроса,"#ТаблицаОбъектаИзменения", ПолноеИмяОбъекта + ".Изменения");
	ТекстЗапроса = СтрЗаменить(ТекстЗапроса,"#ТаблицаОбъекта", ПолноеИмяОбъекта);
	УстановитьРазмерВыборки(ТекстЗапроса, ДополнительныеПараметры);
	МенеджерВременныхТаблиц = Новый МенеджерВременныхТаблиц();
	СоздатьВременнуюТаблицуЗаблокированныхДляЧтенияИИзмененияДанных(Очередь, ПолноеИмяОбъекта, МенеджерВременныхТаблиц);
	
	ДобавитьПроверкуБлокировкиДополнительныхИсточников(Очередь, ТекстЗапроса, ПолноеИмяОбъекта, Неопределено, МенеджерВременныхТаблиц, Ложь, ДополнительныеПараметры);
	
	Запрос = Новый Запрос;
	Запрос.Текст = ТекстЗапроса;
	Запрос.МенеджерВременныхТаблиц = МенеджерВременныхТаблиц;
	Запрос.УстановитьПараметр("ТекущаяОчередь", ОчередьСсылкой(Очередь));
	Если ФильтрАктуальныхДанных.ЕстьФильтр Тогда
		Запрос.УстановитьПараметр("ЗначениеОтбораАктуальныхДанных", ФильтрАктуальныхДанных.Значение);
	КонецЕсли;
	
	УстановитьПоляПоСтраницам(Запрос, ПараметрыПостроения);
	Если ЭтоДокумент Или ПараметрыПостроения.ПостраничнаяВыборка Тогда
		УстановитьПорядокПоСтраницам(Запрос, ПараметрыПостроения);
	КонецЕсли;
	
	Возврат ВыбратьДанныеДляОбработки(Запрос, ПараметрыПостроения);
	
КонецФункции

// Создает временную таблицу ссылок, которые не обработаны в текущей очереди
//  и не заблокированы меньшими очередями.
//  Имя таблицы: ВТДляОбработки<ИмяОбъекта>, например ВТДляОбработкиНоменклатура.
//  Колонки таблицы:
//   Ссылка - ЛюбаяСсылка.
//
// Параметры:
//  Очередь           - Число  - очередь обработки, в которой выполняется текущий обработчик.
//  ПолноеИмяОбъекта  - Строка - полное имя объекта, для которого выполняется проверка, например Справочник.Номенклатура.
//  МенеджерВременныхТаблиц - МенеджерВременныхТаблиц - менеджер, в котором будет создана временная таблица.
//  ДополнительныеПараметры - см. ОбновлениеИнформационнойБазы.ДополнительныеПараметрыВыборкиДанныхДляОбработки.
// 
// Возвращаемое значение:
//  Структура - результат формирования временной таблицы:
//   * ЕстьЗаписиВоВременнойТаблице - Булево - в создаваемой таблице есть хотя бы одна запись. Записей может не быть по
//                                            двум причинам:
//                                             все обработано или все, что нужно обработать, еще заблокировано
//                                             обработчиками с меньшей очередью.
//   * ЕстьДанныеДляОбработки - Булево - в очереди есть ссылки для обработки, т.е. еще не все обработано.
//   * ИмяВременнойТаблицы - Строка - имя созданной временной таблицы.
//
Функция СоздатьВременнуюТаблицуСсылокДляОбработки(Очередь, ПолноеИмяОбъекта, МенеджерВременныхТаблиц, ДополнительныеПараметры = Неопределено) Экспорт
	
	Если ДополнительныеПараметры = Неопределено Тогда
		ДополнительныеПараметры = ДополнительныеПараметрыВыборкиДанныхДляОбработки();
	КонецЕсли;
	
	ИмяОбъекта = СтрРазделить(ПолноеИмяОбъекта,".",Ложь)[1];
	МетаданныеОбъекта = ОбщегоНазначения.ОбъектМетаданныхПоПолномуИмени(ПолноеИмяОбъекта);
	ФрагментыЗапроса = Новый Массив;
	
	ФрагментЗапроса =
		"ВЫБРАТЬ ПЕРВЫЕ 1000
		|	ТаблицаОбъекта.Ссылка КАК Ссылка
		|ПОМЕСТИТЬ #ВТДляОбработкиСсылка
		|ИЗ
		|	#ТаблицаОбъектаИзменения КАК ТаблицаИзменений
		|		ВНУТРЕННЕЕ СОЕДИНЕНИЕ #ТаблицаОбъекта КАК ТаблицаОбъекта
		|		ПО ТаблицаИзменений.Ссылка = ТаблицаОбъекта.Ссылка
		|ГДЕ
		|	ТаблицаИзменений.Узел = &ТекущаяОчередь
		|	И НЕ ИСТИНА В (
		|		ВЫБРАТЬ ПЕРВЫЕ 1
		|			ИСТИНА
		|		ИЗ
		|			#ВТЗаблокированоСсылка КАК ВТЗаблокированоСсылка
		|		ГДЕ
		|			ТаблицаОбъекта.Ссылка = ВТЗаблокированоСсылка.Ссылка)
		|	И &УсловиеПоДопИсточникамСсылкам
		|	И &УсловиеПоДопИсточникамРегистрам
		|	И &УсловиеАктуальныеДанные";
	
	Если Не ДополнительныеПараметры.ВыбиратьПорциями Тогда
		ФрагментЗапроса = СтрЗаменить(ФрагментЗапроса, "ВЫБРАТЬ ПЕРВЫЕ 1000", "ВЫБРАТЬ"); // @query-part-1 @query-part-2
	КонецЕсли;
	
	ФильтрАктуальныхДанных = ОбновлениеИнформационнойБазыСлужебный.ФильтрАктуальныхДанных(Неопределено, "ТаблицаОбъекта");
	ФрагментЗапроса = СтрЗаменить(ФрагментЗапроса, "&УсловиеАктуальныеДанные", ФильтрАктуальныхДанных.Условие);
	Если ФильтрАктуальныхДанных.ЕстьФильтр Тогда
		ТекстЗапросаПроверки =
			"ВЫБРАТЬ ПЕРВЫЕ 1
			|	ИСТИНА
			|ИЗ
			|	#ТаблицаОбъектаИзменения КАК ТаблицаИзменений
			|		ВНУТРЕННЕЕ СОЕДИНЕНИЕ #ТаблицаОбъекта КАК ТаблицаОбъекта
			|		ПО ТаблицаИзменений.Ссылка = ТаблицаОбъекта.Ссылка
			|ГДЕ
			|	ТаблицаИзменений.Узел = &ТекущаяОчередь
			|	И &УсловиеАктуальныеДанные";
		ТекстЗапросаПроверки = СтрЗаменить(ТекстЗапросаПроверки, "&УсловиеАктуальныеДанные", ФильтрАктуальныхДанных.Условие);
		ТекстЗапросаПроверки = СтрЗаменить(ТекстЗапросаПроверки,"#ТаблицаОбъектаИзменения", ПолноеИмяОбъекта + ".Изменения");
		ТекстЗапросаПроверки = СтрЗаменить(ТекстЗапросаПроверки,"#ТаблицаОбъекта", ПолноеИмяОбъекта);
		
		ЗапросПроверки = Новый Запрос;
		ЗапросПроверки.УстановитьПараметр("ТекущаяОчередь", ОчередьСсылкой(Очередь));
		ЗапросПроверки.УстановитьПараметр("ЗначениеОтбораАктуальныхДанных", ФильтрАктуальныхДанных.Значение);
		ЗапросПроверки.Текст = ТекстЗапросаПроверки;
		
		ОбработаныВсеАктуальныеДанные = ЗапросПроверки.Выполнить().Пустой();
		
		УстановитьПараметрыОбработчикаПриВыборкеДанных(ДополнительныеПараметры, ОбработаныВсеАктуальныеДанные, Неопределено)
	КонецЕсли;
	
	ФрагментыЗапроса.Добавить(ФрагментЗапроса);
	ЭтоСсылка = ОбщегоНазначения.ЭтоДокумент(МетаданныеОбъекта) Или ОбщегоНазначения.ЭтоЗадача(МетаданныеОбъекта);
	
	Если ДополнительныеПараметры.ВыбиратьПорциями И ЭтоСсылка Тогда
		ФрагментыЗапроса.Добавить("УПОРЯДОЧИТЬ ПО
			|	ТаблицаОбъекта.Дата УБЫВ");
	КонецЕсли;
	
	ФрагментЗапроса =
		"ИНДЕКСИРОВАТЬ ПО
		|	Ссылка
		|;
		|
		|////////////////////////////////////////////////////////////////////////////////
		|УНИЧТОЖИТЬ #ВТЗаблокированоСсылка";
	ФрагментыЗапроса.Добавить(ФрагментЗапроса);
	ТекстЗапроса = СтрСоединить(ФрагментыЗапроса, Символы.ПС);
	
	Если ПустаяСтрока(ДополнительныеПараметры.ИмяВременнойТаблицы) Тогда
		ИмяВременнойТаблицы = "ВТДляОбработки" + ИмяОбъекта;
	Иначе
		ИмяВременнойТаблицы = ДополнительныеПараметры.ИмяВременнойТаблицы;
	КонецЕсли;
	
	ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "#ВТЗаблокированоСсылка","ВТЗаблокировано" + ИмяОбъекта);
	ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "#ВТДляОбработкиСсылка",ИмяВременнойТаблицы);
	ТекстЗапроса = СтрЗаменить(ТекстЗапроса,"#ТаблицаОбъектаИзменения", ПолноеИмяОбъекта + ".Изменения");	
	
	СоздатьВременнуюТаблицуЗаблокированныхДляЧтенияИИзмененияДанных(Очередь, ПолноеИмяОбъекта, МенеджерВременныхТаблиц);
	
	ДобавитьПроверкуБлокировкиДополнительныхИсточников(Очередь, ТекстЗапроса, ПолноеИмяОбъекта, Неопределено, МенеджерВременныхТаблиц, Истина, ДополнительныеПараметры);
	
	ТекстЗапроса = СтрЗаменить(ТекстЗапроса,"#ТаблицаОбъекта", ПолноеИмяОбъекта);	
	
	ТекущаяОчередь = ОчередьСсылкой(Очередь);
	
	Запрос = Новый Запрос;
	Запрос.Текст = ТекстЗапроса;
	Запрос.МенеджерВременныхТаблиц = МенеджерВременныхТаблиц;
	Запрос.УстановитьПараметр("ТекущаяОчередь", ТекущаяОчередь);
	РезультатЗапроса = Запрос.ВыполнитьПакет();
	
	Результат = Новый Структура("ЕстьЗаписиВоВременнойТаблице,ЕстьДанныеДляОбработки,ИмяВременнойТаблицы", Ложь, Ложь,"");
	Результат.ИмяВременнойТаблицы = ИмяВременнойТаблицы;
	Результат.ЕстьЗаписиВоВременнойТаблице = РезультатЗапроса[0].Выгрузить()[0].Количество <> 0;
	
	Если Результат.ЕстьЗаписиВоВременнойТаблице Тогда
		Результат.ЕстьДанныеДляОбработки = Истина;
	Иначе
		Результат.ЕстьДанныеДляОбработки = ЕстьДанныеДляОбработки(Очередь, ПолноеИмяОбъекта);
	КонецЕсли;	
		
	Возврат Результат;
	
КонецФункции

// Возвращает значения измерений независимого регистра сведений для обработки.
// Данные берутся из зарегистрированных в очереди, учитываются заблокированные более приоритетными очередями данные.
//
// Параметры:
//  Очередь           - Число - очередь, к которой отнесен обработчик и в которой зарегистрированы данные, которые он будет
//                              обрабатывать.
//  ПолноеИмяОбъекта  - Строка - имя объекта, который нужно обработать. Например, РегистрСведений.ШтрихкодыНоменклатуры.
//  ДополнительныеПараметры - см. ОбновлениеИнформационнойБазы.ДополнительныеПараметрыВыборкиДанныхДляОбработки.
// 
// Возвращаемое значение:
//   ВыборкаИзРезультатаЗапроса - выборка значений измерений, которые нужно обработать, имена полей соответствует именам
//                                 измерений регистра. Если по измерению не регистрировалась необходимость обработки,
//                                 то в выборке по этому измерению будет пустое значение.
//   ТаблицаЗначений - данные, которые нужно обработать, имена колонок соответствуют именам измерений регистра.
//
Функция ВыбратьИзмеренияНезависимогоРегистраСведенийДляОбработки(Очередь, ПолноеИмяОбъекта, ДополнительныеПараметры = Неопределено) Экспорт
	Если ДополнительныеПараметры = Неопределено Тогда
		ДополнительныеПараметры = ДополнительныеПараметрыВыборкиДанныхДляОбработки();
	КонецЕсли;
	
	ПараметрыОбработчикаОбновления = Неопределено;
	ДополнительныеПараметры.Свойство("ПараметрыОбработчикаОбновления", ПараметрыОбработчикаОбновления);
	
	ИмяОбъекта = СтрРазделить(ПолноеИмяОбъекта,".",Ложь)[1];
	МетаданныеОбъекта = ОбщегоНазначения.ОбъектМетаданныхПоПолномуИмени(ПолноеИмяОбъекта);
	ПараметрыПостроения = ПараметрыПостроенияВыборки(ДополнительныеПараметры, "ТаблицаИзменений");
	ЗаданыПоляУпорядочивания = ЗаданыПоляУпорядочивания(ДополнительныеПараметры);
	НеобходимоУпорядочивание = ЗаданыПоляУпорядочивания Или ПараметрыПостроения.ПостраничнаяВыборка;
	
	Запрос = Новый Запрос;
	ТекстЗапроса =
		"ВЫБРАТЬ ПЕРВЫЕ 1000
		|	&ВыбираемыеПоля
		|ИЗ
		|	#ТаблицаОбъектаИзменения КАК ТаблицаИзменений
		|ГДЕ
		|	ТаблицаИзменений.Узел = &ТекущаяОчередь
		|	И &ТекстУсловияПоОтборуНезаблокированных
		|	И &УсловиеПоДопИсточникамСсылкам
		|	И &УсловиеПоСтраницам
		|	И &УсловиеАктуальныеДанные";
	
	Если НеобходимоУпорядочивание Тогда
		УстановитьПоляУпорядочиванияНезависимогоРегистраСведений(ПараметрыПостроения);
		ТекстЗапроса = ТекстЗапроса + "
			|УПОРЯДОЧИТЬ ПО
			|	&ПорядокВыборки
			|";
	КонецЕсли;
	
	ФильтрАктуальныхДанных = ОбновлениеИнформационнойБазыСлужебный.ФильтрАктуальныхДанных(ПараметрыОбработчикаОбновления, "ТаблицаИзменений");
	ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&УсловиеАктуальныеДанные", ФильтрАктуальныхДанных.Условие);
	Если ФильтрАктуальныхДанных.ЕстьФильтр Тогда
		ТекстЗапросаПроверки =
			"ВЫБРАТЬ ПЕРВЫЕ 1
			|	ИСТИНА
			|ИЗ
			|	#ТаблицаОбъектаИзменения КАК ТаблицаИзменений
			|ГДЕ
			|	ТаблицаИзменений.Узел = &ТекущаяОчередь
			|	И &УсловиеАктуальныеДанные";
		ТекстЗапросаПроверки = СтрЗаменить(ТекстЗапросаПроверки, "&УсловиеАктуальныеДанные", ФильтрАктуальныхДанных.Условие);
		ТекстЗапросаПроверки = СтрЗаменить(ТекстЗапросаПроверки,"#ТаблицаОбъектаИзменения", ПолноеИмяОбъекта + ".Изменения");
		
		ЗапросПроверки = Новый Запрос;
		ЗапросПроверки.УстановитьПараметр("ТекущаяОчередь", ОчередьСсылкой(Очередь));
		ЗапросПроверки.УстановитьПараметр("ЗначениеОтбораАктуальныхДанных", ФильтрАктуальныхДанных.Значение);
		ЗапросПроверки.Текст = ТекстЗапросаПроверки;
		
		ОбработаныВсеАктуальныеДанные = ЗапросПроверки.Выполнить().Пустой();
		УстановитьПараметрыОбработчикаПриВыборкеДанных(ДополнительныеПараметры, ОбработаныВсеАктуальныеДанные, ПараметрыОбработчикаОбновления)
	КонецЕсли;
	
	ИзмеренияСОсновнымОтбором = Новый Массив;
	
	Для Каждого Измерение Из МетаданныеОбъекта.Измерения Цикл
		Если Не Измерение.ОсновнойОтбор Тогда
			Продолжить;
		КонецЕсли;
		
		УстановитьИзмерение(ПараметрыПостроения, Измерение.Имя);
		ИзмеренияСОсновнымОтбором.Добавить(Измерение.Имя);
		Запрос.УстановитьПараметр("ПустоеЗначениеИзмерения"+ Измерение.Имя, Измерение.Тип.ПривестиЗначение()); 
	КонецЦикла;
	
	ПризнакНепериодический = Метаданные.СвойстваОбъектов.ПериодичностьРегистраСведений.Непериодический;
	Если МетаданныеОбъекта.ПериодичностьРегистраСведений <> ПризнакНепериодический
		И МетаданныеОбъекта.ОсновнойОтборПоПериоду Тогда
		УстановитьПериод(ПараметрыПостроения);
	КонецЕсли;
	
	УстановитьРесурсы(ПараметрыПостроения, МетаданныеОбъекта.Ресурсы);
	УстановитьРеквизиты(ПараметрыПостроения, МетаданныеОбъекта.Реквизиты);
	
	ТекстУсловияПоОтборуНезаблокированных = УсловиеОтбораНезаблокированныхИзмерений(ИзмеренияСОсновнымОтбором);
	ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&ТекстУсловияПоОтборуНезаблокированных", ТекстУсловияПоОтборуНезаблокированных);
	ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "#ТаблицаОбъектаИзменения", ПолноеИмяОбъекта + ".Изменения");
	ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "#ВТЗаблокированоИзмерения","ВТЗаблокировано" + ИмяОбъекта);
	УстановитьРазмерВыборки(ТекстЗапроса, ДополнительныеПараметры);
	
	МенеджерВременныхТаблиц = Новый МенеджерВременныхТаблиц();
	
	СоздатьВременнуюТаблицуЗаблокированныхДляЧтенияИИзмененияДанных(Очередь, ПолноеИмяОбъекта, МенеджерВременныхТаблиц);
	
	ДобавитьПроверкуБлокировкиДополнительныхИсточниковДляНезависимогоРегистра(Очередь,
																				ТекстЗапроса,
																				ПолноеИмяОбъекта,
																				МенеджерВременныхТаблиц,
																				ДополнительныеПараметры);	
	
	Запрос.Текст = ТекстЗапроса;
	Запрос.МенеджерВременныхТаблиц = МенеджерВременныхТаблиц;
	Запрос.УстановитьПараметр("ТекущаяОчередь", ОчередьСсылкой(Очередь));
	Если ФильтрАктуальныхДанных.ЕстьФильтр Тогда
		Запрос.УстановитьПараметр("ЗначениеОтбораАктуальныхДанных", ФильтрАктуальныхДанных.Значение);
	КонецЕсли;
	
	УстановитьПоляПоСтраницам(Запрос, ПараметрыПостроения);
	Если НеобходимоУпорядочивание Тогда
		УстановитьПорядокПоСтраницам(Запрос, ПараметрыПостроения);
	КонецЕсли;
	
	Возврат ВыбратьДанныеДляОбработки(Запрос, ПараметрыПостроения);
	
КонецФункции

// Создает временную таблицу значения измерений независимого регистра сведений для обработки.
//  Имя таблицы: ВТДляОбработки<ИмяОбъекта>, например ВТДляОбработкиШтрихкодыНоменклатуры.
//  Колонки таблицы соответствуют измерениям регистра. Если по измерению не регистрировалась 
//	необходимость обработки, то в выборке по этому измерению будет пустое значение.
//
// Параметры:
//  Очередь					 - Число					 - очередь обработки, в которой выполняется текущий обработчик.
//  ПолноеИмяОбъекта		 - Строка					 - полное имя объекта, для которого выполняется проверка, например Справочник.Номенклатура.
//  МенеджерВременныхТаблиц	 - МенеджерВременныхТаблиц	 - менеджер, в котором будет создана временная таблица.
//  ДополнительныеПараметры	 - см. ОбновлениеИнформационнойБазы.ДополнительныеПараметрыВыборкиДанныхДляОбработки.
// 
// Возвращаемое значение:
//  Структура - результат формирования временной таблицы:
//   * ЕстьЗаписиВоВременнойТаблице - Булево - в создаваемой таблице есть хотя бы одна запись. Записей может не быть по
//                                            двум причинам:
//                                              все обработано или все, что нужно обработать, еще заблокировано
//                                              обработчиками с меньшей очередью.
//   * ЕстьДанныеДляОбработки - Булево - в очереди есть данные для обработки, т.е. еще не все обработано.
//   * ИмяВременнойТаблицы - Строка - имя созданной временной таблицы.
//
Функция СоздатьВременнуюТаблицуИзмеренийНезависимогоРегистраСведенийДляОбработки(Очередь, ПолноеИмяОбъекта, МенеджерВременныхТаблиц, ДополнительныеПараметры = Неопределено) Экспорт
	
	Если ДополнительныеПараметры = Неопределено Тогда
		ДополнительныеПараметры = ДополнительныеПараметрыВыборкиДанныхДляОбработки();
	КонецЕсли;
	
	ИмяОбъекта = СтрРазделить(ПолноеИмяОбъекта,".",Ложь)[1];
	МетаданныеОбъекта = ОбщегоНазначения.ОбъектМетаданныхПоПолномуИмени(ПолноеИмяОбъекта);
	
	Запрос = Новый Запрос;
	ТекстЗапроса =
		"ВЫБРАТЬ ПЕРВЫЕ 1000
		|	&ТекстВыборкиИзмерений
		|ПОМЕСТИТЬ #ВТДляОбработкиИзмерения
		|ИЗ
		|	#ТаблицаОбъектаИзменения КАК ТаблицаИзменений
		|ГДЕ
		|	ТаблицаИзменений.Узел = &ТекущаяОчередь
		|	И &ТекстУсловияПоОтборуНезаблокированных
		|	И &УсловиеПоДопИсточникамСсылкам
		|ИНДЕКСИРОВАТЬ ПО
		|	&ИндексируемыеИзмерения
		|;
		|
		|////////////////////////////////////////////////////////////////////////////////
		|УНИЧТОЖИТЬ #ВТЗаблокированоИзмерения";
	
	Если Не ДополнительныеПараметры.ВыбиратьПорциями Тогда
		ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "ВЫБРАТЬ ПЕРВЫЕ 1000", "ВЫБРАТЬ"); // @query-part-1 @query-part-2
	КонецЕсли;
	
	ИзмеренияСОсновнымОтбором = Новый Массив;
	ОтбираемыеИзмерения = Новый Массив;
	ШаблонВыбираемогоИзмерения = "ТаблицаИзменений.%1 КАК %1";
	
	РегистрПериодический = 
		(МетаданныеОбъекта.ПериодичностьРегистраСведений <> Метаданные.СвойстваОбъектов.ПериодичностьРегистраСведений.Непериодический)
		И МетаданныеОбъекта.ОсновнойОтборПоПериоду;
	Для Каждого Измерение Из МетаданныеОбъекта.Измерения Цикл
		Если Не Измерение.ОсновнойОтбор Тогда
			Продолжить;
		КонецЕсли;
		
		ВыбираемоеИзмерение = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(ШаблонВыбираемогоИзмерения,
			Измерение.Имя);
		ОтбираемыеИзмерения.Добавить(ВыбираемоеИзмерение);
		ИзмеренияСОсновнымОтбором.Добавить(Измерение.Имя);
		Запрос.УстановитьПараметр("ПустоеЗначениеИзмерения"+ Измерение.Имя, Измерение.Тип.ПривестиЗначение()); 
	КонецЦикла;
	
	Если ПустаяСтрока(ДополнительныеПараметры.ИмяВременнойТаблицы) Тогда
		ИмяВременнойТаблицы = "ВТДляОбработки" + ИмяОбъекта;
	Иначе
		ИмяВременнойТаблицы = ДополнительныеПараметры.ИмяВременнойТаблицы;
	КонецЕсли;
	
	ТекстУсловияПоОтборуНезаблокированных = УсловиеОтбораНезаблокированныхИзмерений(ИзмеренияСОсновнымОтбором);
	
	Если РегистрПериодический Тогда
		ИзмеренияСОсновнымОтбором.Добавить("Период");
	КонецЕсли;
	
	ТекстВыборкиИзмерений = ПоляДляЗапроса(ИзмеренияСОсновнымОтбором,, "ТаблицаИзменений");
	ТекстИндексируемыхИзмерений = УпорядочиванияДляЗапроса(ИзмеренияСОсновнымОтбором);
	ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&ТекстВыборкиИзмерений", ТекстВыборкиИзмерений);
	ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&ИндексируемыеИзмерения", ТекстИндексируемыхИзмерений);
	ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&ТекстУсловияПоОтборуНезаблокированных", ТекстУсловияПоОтборуНезаблокированных);
	ТекстЗапроса = СтрЗаменить(ТекстЗапроса,"#ТаблицаОбъектаИзменения", ПолноеИмяОбъекта + ".Изменения");	
	ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "#ВТЗаблокированоИзмерения","ВТЗаблокировано" + ИмяОбъекта);
	ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "#ВТДляОбработкиИзмерения",ИмяВременнойТаблицы);
	
	СоздатьВременнуюТаблицуЗаблокированныхДляЧтенияИИзмененияДанных(Очередь, ПолноеИмяОбъекта, МенеджерВременныхТаблиц);
	ДобавитьПроверкуБлокировкиДополнительныхИсточниковДляНезависимогоРегистра(Очередь,
																				ТекстЗапроса,
																				ПолноеИмяОбъекта,
																				МенеджерВременныхТаблиц,
																				ДополнительныеПараметры);	
	
	ТекущаяОчередь = ОчередьСсылкой(Очередь);
	
	Запрос.Текст = ТекстЗапроса;
	Запрос.МенеджерВременныхТаблиц = МенеджерВременныхТаблиц;
	Запрос.УстановитьПараметр("ТекущаяОчередь", ТекущаяОчередь);
	РезультатЗапроса = Запрос.ВыполнитьПакет();
	
	Результат = Новый Структура("ЕстьЗаписиВоВременнойТаблице,ЕстьДанныеДляОбработки,ИмяВременнойТаблицы", Ложь, Ложь,"");
	Результат.ИмяВременнойТаблицы = ИмяВременнойТаблицы;
	Результат.ЕстьЗаписиВоВременнойТаблице = РезультатЗапроса[0].Выгрузить()[0].Количество <> 0;
	
	Если Результат.ЕстьЗаписиВоВременнойТаблице Тогда
		Результат.ЕстьДанныеДляОбработки = Истина;
	Иначе
		Результат.ЕстьДанныеДляОбработки = ЕстьДанныеДляОбработки(Очередь, ПолноеИмяОбъекта);
	КонецЕсли;	
		
	Возврат Результат;
	
КонецФункции

// Проверяет, есть ли еще необработанные данные.
//
// Параметры:
//  Очередь    - Число        - очередь, к которой отнесен обработчик и в которой зарегистрированы данные,
//                              которые он будет обрабатывать.
//             - Неопределено - проверяется, завершена ли обработка в целом;
//             - Массив       - проверяется, есть данные для обработки в списке очередей.
//  ПолноеИмяМетаданныеОбъекта - Строка
//                             - ОбъектМетаданных - полное имя обрабатываемого объекта или 
//                              его метаданные. Например, "Документ.ПриходныйОрдерНаТовары"
//                             - Массив - массив полных имен объектов или объектов метаданных,
//                              в массиве не должно быть независимых регистров сведений.
//  Отбор - ЛюбаяСсылка
//        - Структура
//        - Неопределено
//        - Массив - отбор данных для проверки.
//                   Если передано Неопределено - проверяется по всему типу объекта.
//                   Если объект - регистр, подчиненный регистратору, то в отборе - ссылка
//                   на регистратор или массив ссылок.
//                   Если объект ссылочного типа, то в отборе - или ссылка, или массив ссылок.
//                   Если объект - независимый регистр сведений, то в отборе - структура со значениями измерений.
//                   Ключ структуры - имя измерения, значение - значение отбора (можно передать массив значений).
//
// Возвращаемое значение:
//  Булево - Истина, если еще не все данные обработаны.
//
Функция ЕстьДанныеДляОбработки(Очередь, ПолноеИмяМетаданныеОбъекта, Отбор = Неопределено) Экспорт
	
	Если ОтложенноеОбновлениеЗавершено() Тогда
		Возврат Ложь;
	КонецЕсли;
	
	Если ТипЗнч(ПолноеИмяМетаданныеОбъекта) = Тип("Строка") Тогда
		ПолныеИменаОбрабатываемыхОбъектов = СтрРазделить(ПолноеИмяМетаданныеОбъекта, ",");
	ИначеЕсли ТипЗнч(ПолноеИмяМетаданныеОбъекта) = Тип("Массив") Тогда
		ПолныеИменаОбрабатываемыхОбъектов = ПолноеИмяМетаданныеОбъекта;
	ИначеЕсли ТипЗнч(ПолноеИмяМетаданныеОбъекта) = Тип("ОбъектМетаданных") Тогда
		ПолныеИменаОбрабатываемыхОбъектов = Новый Массив;
		ПолныеИменаОбрабатываемыхОбъектов.Добавить(ПолноеИмяМетаданныеОбъекта.ПолноеИмя());
	Иначе
		ТекстИсключения = НСтр("ru = 'Передан неправильный тип параметра ""%1"" в функцию %2'");
		ТекстИсключения = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(ТекстИсключения, "ПолноеИмяМетаданныеОбъекта", "ОбновлениеИнформационнойБазы.ЕстьДанныеДляОбработки");
		ВызватьИсключение ТекстИсключения;
	КонецЕсли;	
	
	Запрос = Новый Запрос;
	
	ТекстыЗапроса = Новый Массив;
	ТекстыЗапросаРегистров = Новый Массив;
	ОтборУстановлен = Ложь;
	
	Для Каждого ОбрабатываемыйТип Из ПолныеИменаОбрабатываемыхОбъектов Цикл
		
		Если ТипЗнч(ОбрабатываемыйТип) = Тип("ОбъектМетаданных") Тогда
			МетаданныеОбъекта = ОбрабатываемыйТип;
			ПолноеИмяОбъекта  = ОбрабатываемыйТип.ПолноеИмя();
		Иначе
			МетаданныеОбъекта = ОбщегоНазначения.ОбъектМетаданныхПоПолномуИмени(ОбрабатываемыйТип);
			ПолноеИмяОбъекта  = ОбрабатываемыйТип;
		КонецЕсли;
		ПроверкаРегистра = Ложь;
		
		ИмяОбъекта = СтрРазделить(ПолноеИмяОбъекта,".",Ложь)[1];
		
		УсловиеОтбораДанных = "ИСТИНА";
		УсловиеПроверкиРегистратора = "ИСТИНА";
		
		
		Если ОбщегоНазначения.ЭтоОбъектСсылочногоТипа(МетаданныеОбъекта) Тогда
			ТекстЗапроса =
			"ВЫБРАТЬ ПЕРВЫЕ 1
			|	ТаблицаИзменений.Ссылка КАК Ссылка
			|ИЗ
			|	#ТаблицаИзменений КАК ТаблицаИзменений
			|ГДЕ
			|	&УсловиеОтбораУзла
			|	И &УсловиеОтбораДанных
			|	И ИСТИНА В (
			|		ВЫБРАТЬ ПЕРВЫЕ 1
			|			ИСТИНА
			|		ИЗ
			|			#ТаблицаОбъекта КАК Таблица
			|		ГДЕ
			|			Таблица.Ссылка = ТаблицаИзменений.Ссылка)";
			
			Запрос.УстановитьПараметр("Ссылка", Отбор);
			
			Если Отбор <> Неопределено Тогда
				УсловиеОтбораДанных = "ТаблицаИзменений.Ссылка В (&Отбор)";
			КонецЕсли;
			
		ИначеЕсли ОбщегоНазначения.ЭтоРегистрСведений(МетаданныеОбъекта)
			И МетаданныеОбъекта.РежимЗаписи = Метаданные.СвойстваОбъектов.РежимЗаписиРегистра.Независимый Тогда
			
			Если ПолныеИменаОбрабатываемыхОбъектов.Количество() > 1 Тогда
				ТекстИсключения = НСтр("ru = 'В массиве имен в параметре ""%1"" в функцию %2 передан независимый регистр сведений.'");
				ТекстИсключения = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(ТекстИсключения,
					"ПолноеИмяМетаданныеОбъекта", "ОбновлениеИнформационнойБазы.ЕстьДанныеДляОбработки");
				ВызватьИсключение ТекстИсключения;
			КонецЕсли;	
			
			ОтборУстановлен = Истина;
			
			ТекстЗапроса =
			"ВЫБРАТЬ ПЕРВЫЕ 1
			|	&ТекстВыборкиИзмерений
			|ИЗ
			|	#ТаблицаИзменений КАК ТаблицаИзменений
			|ГДЕ
			|	&УсловиеОтбораУзла
			|	И &УсловиеОтбораДанных";
			
			ТекстВыборкиИзмерений = "";
			Для Каждого Измерение Из МетаданныеОбъекта.Измерения Цикл
				Если Не Измерение.ОсновнойОтбор Тогда
					Продолжить;
				КонецЕсли;
				
				ТекстВыборкиИзмерений = ТекстВыборкиИзмерений + "
				|	ТаблицаИзменений." + Измерение.Имя + " КАК " + Измерение.Имя + ",";
				
				Если Отбор <> Неопределено Тогда
					УсловиеОтбораДанных = УсловиеОтбораДанных + "
					|	И (ТаблицаИзменений." + Измерение.Имя + " В (&ЗначениеОтбора" + Измерение.Имя + ")
					|		ИЛИ ТаблицаИзменений." + Измерение.Имя + " = &ПустоеЗначение" + Измерение.Имя + ")";
					
					Если Отбор.Свойство(Измерение.Имя) Тогда
						Запрос.УстановитьПараметр("ЗначениеОтбора" + Измерение.Имя, Отбор[Измерение.Имя]);
					Иначе
						Запрос.УстановитьПараметр("ЗначениеОтбора" + Измерение.Имя, Измерение.Тип.ПривестиЗначение());
					КонецЕсли;
					
					Запрос.УстановитьПараметр("ПустоеЗначение" + Измерение.Имя, Измерение.Тип.ПривестиЗначение());
				КонецЕсли;
			КонецЦикла;
			
			Если ПустаяСтрока(ТекстВыборкиИзмерений) Тогда
				ТекстВыборкиИзмерений = "*";
			Иначе
				ТекстВыборкиИзмерений = Лев(ТекстВыборкиИзмерений, СтрДлина(ТекстВыборкиИзмерений) - 1);
			КонецЕсли;
			
			ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&ТекстВыборкиИзмерений", ТекстВыборкиИзмерений);
			
		ИначеЕсли ОбщегоНазначения.ЭтоРегистр(МетаданныеОбъекта) Тогда
			ПроверкаРегистра = Истина;
			
			ШаблонЗапросаПроверки = 
				"ИСТИНА В (ВЫБРАТЬ ПЕРВЫЕ 1
				|	ИСТИНА
				|ИЗ
				|	#ИмяТаблицы КАК ПроверяемаяТаблица
				|ГДЕ
				|	ТаблицаИзменений.Регистратор = ПроверяемаяТаблица.Ссылка)";
			
			КоллекцияЗапросов = Новый Массив;
			ТипыРегистратора = МетаданныеОбъекта.СтандартныеРеквизиты.Регистратор.Тип;
			ТипыРегистратора = ТипыРегистратора.Типы();
			Для Каждого ТипРегистратора Из ТипыРегистратора Цикл
				МетаданныеРегистратора = Метаданные.НайтиПоТипу(ТипРегистратора);
				Если МетаданныеРегистратора = Неопределено Тогда
					Продолжить;
				КонецЕсли;
				ПолноеИмяРегистратора = МетаданныеРегистратора.ПолноеИмя();
				ТекстЗапросаПроверки = СтрЗаменить(ШаблонЗапросаПроверки, "#ИмяТаблицы", ПолноеИмяРегистратора);
				КоллекцияЗапросов.Добавить(ТекстЗапросаПроверки);
			КонецЦикла;
			
			УсловиеПроверкиРегистратора = СтрСоединить(КоллекцияЗапросов, Символы.ПС + "ИЛИ ");
			
			ТекстЗапроса =
			"ВЫБРАТЬ ПЕРВЫЕ 1
			|	ТаблицаИзменений.Регистратор КАК Ссылка
			|ИЗ
			|	#ТаблицаИзменений КАК ТаблицаИзменений
			|ГДЕ
			|	&УсловиеОтбораУзла
			|	И &УсловиеОтбораДанных
			|	И (&УсловиеПоРегистраторам)";
			
			Если Отбор <> Неопределено Тогда
				УсловиеОтбораДанных = "ТаблицаИзменений.Регистратор В (&Отбор)";
			КонецЕсли;
			
		Иначе
			ТекстИсключения = НСтр("ru = 'Для типа метаданных ""%1"" не поддерживается проверка в функции %2'");
			ТекстИсключения = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(ТекстИсключения,
				Строка(МетаданныеОбъекта), "ОбновлениеИнформационнойБазы.ЕстьДанныеДляОбработки");
			ВызватьИсключение ТекстИсключения;
		КонецЕсли;
		
		ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "#ТаблицаИзменений", ПолноеИмяОбъекта + ".Изменения");
		ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "#ТаблицаОбъекта", ПолноеИмяОбъекта);
		ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "#ИмяОбъекта", ИмяОбъекта);
		ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&УсловиеОтбораДанных", УсловиеОтбораДанных);
		ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&УсловиеПоРегистраторам", УсловиеПроверкиРегистратора);
		
		Если ПроверкаРегистра Тогда
			ТекстыЗапросаРегистров.Добавить(ТекстЗапроса);
		Иначе
			ТекстыЗапроса.Добавить(ТекстЗапроса);
		КонецЕсли;
		
	КонецЦикла;
	
	Соединитель = "
	|
	|ОБЪЕДИНИТЬ ВСЕ
	|";

	ТекстЗапроса = СтрСоединить(ТекстыЗапроса, Соединитель);
	
	ИтоговыйМассивЗапросов = Новый Массив;
	ИтоговыйМассивЗапросов.Добавить(ТекстЗапроса);
	ОбщегоНазначенияКлиентСервер.ДополнитьМассив(ИтоговыйМассивЗапросов, ТекстыЗапросаРегистров);
	
	Для Каждого ТекстЗапроса Из ИтоговыйМассивЗапросов Цикл
		Если Не ЗначениеЗаполнено(ТекстЗапроса) Тогда
			Продолжить;
		КонецЕсли;
		
		Если Очередь = Неопределено Тогда
			УсловиеОтбораУзла = "	ТаблицаИзменений.Узел ССЫЛКА ПланОбмена.ОбновлениеИнформационнойБазы ";
		Иначе
			УсловиеОтбораУзла = "	ТаблицаИзменений.Узел В (&Узлы) ";
			Если ТипЗнч(Очередь) = Тип("Массив") Тогда
				Запрос.УстановитьПараметр("Узлы", Очередь);
			Иначе
				Запрос.УстановитьПараметр("Узлы", ОчередьСсылкой(Очередь));
			КонецЕсли;
		КонецЕсли;
		
		ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&УсловиеОтбораУзла", УсловиеОтбораУзла);
		
		Если Не ОтборУстановлен Тогда
			Запрос.УстановитьПараметр("Отбор", Отбор);
		КонецЕсли;
			
		Запрос.Текст = ТекстЗапроса;
		
		УстановитьОтключениеБезопасногоРежима(Истина);
		УстановитьПривилегированныйРежим(Истина);
		Если Не Запрос.Выполнить().Пустой() Тогда // @skip-check query-in-loop - для избежания слишком тяжелого запроса.
			Возврат Истина;
		КонецЕсли;
		УстановитьПривилегированныйРежим(Ложь);
		УстановитьОтключениеБезопасногоРежима(Ложь);
	КонецЦикла; 
	
	Возврат Ложь;
КонецФункции

// Проверяет, что все данные обработаны.
//
// Параметры:
//  Очередь    - Число        - очередь, к которой отнесен обработчик и в которой зарегистрированы данные,
//                              которые он будет обрабатывать.
//             - Неопределено - проверяется, завершена ли обработка в целом;
//             - Массив       - проверяется, есть ли данные для обработки в списке очередей.
//  ПолноеИмяМетаданныеОбъекта - Строка
//                             - ОбъектМетаданных - полное имя обрабатываемого объекта или 
//                              его метаданные. Например, "Документ.ПриходныйОрдерНаТовары"
//                             - Массив - массив полных имен объектов или объектов метаданных,
//                              в массиве не должно быть независимых регистров сведений.
//  Отбор - ЛюбаяСсылка
//        - Структура
//        - Неопределено
//        - Массив - отбор данных для проверки.
//                   Если передано Неопределено - проверяется по всему типу объекта.
//                   Если объект - регистр, подчиненный регистратору, то в отборе - ссылка
//                   на регистратор или массив ссылок.
//                   Если объект ссылочного типа, то в отборе - или ссылка, или массив ссылок.
//                   Если объект - независимый регистр сведений, то в отборе - структура со значениями измерений.
//                   Ключ структуры - имя измерения, значение - значение отбора (можно передать массив значений).
// 
// Возвращаемое значение:
//  Булево - Истина, если все данные обработаны.
//
Функция ОбработкаДанныхЗавершена(Очередь, ПолноеИмяМетаданныеОбъекта, Отбор = Неопределено) Экспорт
	
	Возврат Не ЕстьДанныеДляОбработки(Очередь, ПолноеИмяМетаданныеОбъекта, Отбор);
	
КонецФункции

// Проверяет, есть ли заблокированные меньшими очередями обработки данные.
//
// Параметры:
//  Очередь - Число
//          - Неопределено - очередь, к которой отнесен обработчик и в которой
//                           зарегистрированы данные, которые он будет обрабатывать.
//  ПолноеИмяМетаданныеОбъекта - Строка
//                             - ОбъектМетаданных - полное имя обрабатываемого объекта или
//                                        его метаданные. Например, "Документ.ПриходныйОрдерНаТовары"
//                             - Массив - массив полных имен объектов или объектов метаданных,
//                                        в массиве не должно быть независимых регистров сведений.
// 
// Возвращаемое значение:
//  Булево - Истина, если данный объект заблокирован для обработки меньшими очередями.
//
Функция ЕстьЗаблокированныеПредыдущимиОчередямиДанные(Очередь, ПолноеИмяМетаданныеОбъекта) Экспорт
	
	Возврат ЕстьДанныеДляОбработки(УзлыМеньшейОчереди(Очередь), ПолноеИмяМетаданныеОбъекта);
	
КонецФункции

// Проверяет, завершилась ли обработка данных обработчиками, отнесенными на более раннюю очередь.
//
// Параметры:
//  Очередь    - Число        - очередь, к которой отнесен обработчик и в которой зарегистрированы данные,
//                              которые он будет обрабатывать.
//             - Неопределено - проверяется, завершена ли обработка в целом;
//             - Массив       - проверяется, есть ли данные для обработки в списке очередей.
//  Данные     - ЛюбаяСсылка
//             - РегистрСведенийНаборЗаписей, РегистрНакопленияНаборЗаписей
//             - РегистрБухгалтерииНаборЗаписей, РегистрРасчетаНаборЗаписей
//             - СправочникОбъект, ДокументОбъект, ПланВидовХарактеристикОбъект, БизнесПроцессОбъект, ЗадачаОбъект
//             - ДанныеФормыСтруктура - ссылка на объект, сам объект
//                              или набор записей, который необходимо проверить.
//                              Если ДополнительныеПараметры.ЭтоДвижения = Истина, то Данные - это регистратор
//                              указанного в ДополнительныеПараметры регистра.
//  ДополнительныеПараметры   - см. ОбновлениеИнформационнойБазы.ДополнительныеПараметрыОтметкиОбработки.
//  МетаданныеИОтбор          - см. ОбновлениеИнформационнойБазы.МетаданныеИОтборПоДанным.
// 
// Возвращаемое значение:
//  Булево - Истина, если переданный объект обновлен на новую версию и его можно изменять.
//
Функция МожноЧитатьИМенять(Очередь, Данные, ДополнительныеПараметры = Неопределено, МетаданныеИОтбор = Неопределено) Экспорт
	
	Если ОтложенноеОбновлениеЗавершено() Тогда
		Возврат Истина;
	КонецЕсли;
	
	Если МетаданныеИОтбор = Неопределено Тогда
		МетаданныеИОтбор = МетаданныеИОтборПоДанным(Данные, ДополнительныеПараметры);
	КонецЕсли;
	
	Если МетаданныеИОтбор.ЭтоНовый Тогда
		Возврат Истина;
	КонецЕсли;
	
	Если Очередь = Неопределено Тогда
		Возврат Не ЕстьДанныеДляОбработки(Неопределено, МетаданныеИОтбор.Метаданные, МетаданныеИОтбор.Отбор);
	Иначе
		Возврат Не ЕстьДанныеДляОбработки(УзлыМеньшейОчереди(Очередь), МетаданныеИОтбор.Метаданные, МетаданныеИОтбор.Отбор);
	КонецЕсли;
	
КонецФункции

// Создает временную таблицу заблокированных данных.
// Имя таблицы: ВТЗаблокированы<ИмяОбъекта>, например ВТЗаблокированоНоменклатура.
//  Колонки таблицы:
//      для объектов ссылочного типа
//          * Ссылка;
//      для регистров, подчиненных регистратору
//          * Регистратор;
//      для регистров с непосредственной записью
//          * колонки, соответствующие измерениям регистра.
//
// Параметры:
//  Очередь                 - Число
//                          - Неопределено - очередь обработки, в которой выполняется текущий обработчик.
//                             Если передано Неопределено, то проверяется во всех очередях.
//  ПолноеИмяОбъекта        - Строка - полное имя объекта, для которого выполняется проверка,
//                             например Справочник.Номенклатура.
//  МенеджерВременныхТаблиц - МенеджерВременныхТаблиц - менеджер, в котором будет создана временная таблица.
//  ДополнительныеПараметры - см. ОбновлениеИнформационнойБазы.ДополнительныеПараметрыВыборкиДанныхДляОбработки
//
// Возвращаемое значение:
//  Структура:
//     * ЕстьЗаписиВоВременнойТаблице - Булево - в создаваемой таблице есть хотя бы одна запись.
//     * ИмяВременнойТаблицы          - Строка - имя созданной временной таблицы.
//
Функция СоздатьВременнуюТаблицуЗаблокированныхДляЧтенияИИзмененияДанных(Очередь, ПолноеИмяОбъекта, МенеджерВременныхТаблиц, ДополнительныеПараметры = Неопределено) Экспорт
	
	Если ДополнительныеПараметры = Неопределено Тогда
		ДополнительныеПараметры = ДополнительныеПараметрыВыборкиДанныхДляОбработки();
	КонецЕсли;
	
	Запрос = Новый Запрос;
	Запрос.МенеджерВременныхТаблиц = МенеджерВременныхТаблиц;
	МетаданныеОбъекта = ОбщегоНазначения.ОбъектМетаданныхПоПолномуИмени(ПолноеИмяОбъекта); // ОбъектМетаданныхСправочник, ОбъектМетаданныхДокумент
	ИмяИзмеренияДляОтбора = ДополнительныеПараметры.ИмяИзмеренияДляОтбора;
	ОтложенноеОбновлениеЗавершеноУспешно = ПолучитьФункциональнуюОпцию("ОтложенноеОбновлениеЗавершеноУспешно");
	
	Если ОбщегоНазначения.ЭтоОбъектСсылочногоТипа(МетаданныеОбъекта) Тогда
		Если ОтложенноеОбновлениеЗавершеноУспешно Тогда
			ТекстЗапроса =
			"ВЫБРАТЬ
			|	&ПустоеЗначение КАК Ссылка
			|ПОМЕСТИТЬ #ИмяВременнойТаблицы
			|ГДЕ
			|	ЛОЖЬ";
			                                           
			Запрос.УстановитьПараметр("ПустоеЗначение", МетаданныеОбъекта.СтандартныеРеквизиты.Ссылка.Тип.ПривестиЗначение()); 
		Иначе	
			ТекстЗапроса =
			"ВЫБРАТЬ
			|	ТаблицаИзменений.Ссылка КАК Ссылка
			|ПОМЕСТИТЬ #ИмяВременнойТаблицы
			|ИЗ
			|	#ТаблицаИзменений КАК ТаблицаИзменений
			|ГДЕ
			|	&УсловиеОтбораУзла
			|
			|ИНДЕКСИРОВАТЬ ПО
			|	Ссылка";
		КонецЕсли;
	ИначеЕсли ОбщегоНазначения.ЭтоРегистрСведений(МетаданныеОбъекта)
		И МетаданныеОбъекта.РежимЗаписи = Метаданные.СвойстваОбъектов.РежимЗаписиРегистра.Независимый
		И ВРег(ИмяИзмеренияДляОтбора) = ВРег("Регистратор") Тогда
		ИменаИзмерений = Новый Массив;
		ПсевдонимыИзмерений = Новый Массив;
		
		Если ОтложенноеОбновлениеЗавершеноУспешно Тогда
			ТекстЗапроса =
			"ВЫБРАТЬ
			|	&ТекстВыборкиИзмерений
			|ПОМЕСТИТЬ #ИмяВременнойТаблицы
			|ГДЕ
			|	ЛОЖЬ";
			
			Для Каждого Измерение Из МетаданныеОбъекта.Измерения Цикл
				Если Не Измерение.ОсновнойОтбор Тогда
					Продолжить;
				КонецЕсли;
				
				ИменаИзмерений.Добавить("&ПустоеЗначениеИзмерения" + Измерение.Имя);
				ПсевдонимыИзмерений.Добавить(Измерение.Имя);
				Запрос.УстановитьПараметр("ПустоеЗначениеИзмерения" + Измерение.Имя, Измерение.Тип.ПривестиЗначение());
			КонецЦикла;
			
		Иначе
			ТекстЗапроса =
			"ВЫБРАТЬ
			|	&ТекстВыборкиИзмерений
			|ПОМЕСТИТЬ #ИмяВременнойТаблицы
			|ИЗ
			|	#ТаблицаИзменений КАК ТаблицаИзменений
			|ГДЕ
			|	&УсловиеОтбораУзла
			|ИНДЕКСИРОВАТЬ ПО
			|	&ИндексируемыеИзмерения";
			
			Для Каждого Измерение Из МетаданныеОбъекта.Измерения Цикл
				Если Не Измерение.ОсновнойОтбор Тогда
					Продолжить;
				КонецЕсли;
				
				ИменаИзмерений.Добавить("ТаблицаИзменений." + Измерение.Имя);
				Если НЕ (Измерение.Тип.Типы().Количество() > 1
					И (Измерение.Тип.Типы().Найти(Тип("Булево")) <> Неопределено
						ИЛИ Измерение.Тип.Типы().Найти(Тип("Строка")) <> Неопределено
						ИЛИ Измерение.Тип.Типы().Найти(Тип("Дата")) <> Неопределено)) Тогда
					
					ПсевдонимыИзмерений.Добавить(Измерение.Имя);
				КонецЕсли;
			КонецЦикла;
			
			Если ПсевдонимыИзмерений.Количество() = 0 Тогда
				ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "ИНДЕКСИРОВАТЬ ПО", "");
			КонецЕсли;
			
			ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&ИндексируемыеИзмерения", УпорядочиванияДляЗапроса(ПсевдонимыИзмерений));
		КонецЕсли;
		
		ПризнакНепериодический = Метаданные.СвойстваОбъектов.ПериодичностьРегистраСведений.Непериодический;
		Если МетаданныеОбъекта.ПериодичностьРегистраСведений <> ПризнакНепериодический
			И МетаданныеОбъекта.ОсновнойОтборПоПериоду Тогда
			ИменаИзмерений.Добавить("ТаблицаИзменений.Период");
			ПсевдонимыИзмерений.Добавить("Период");
		КонецЕсли;
		
		Если ИменаИзмерений.Количество() = 0 Тогда
			ТекстВыборкиИзмерений = "*";
		Иначе
			ТекстВыборкиИзмерений = ПоляДляЗапроса(ИменаИзмерений, ПсевдонимыИзмерений);
		КонецЕсли;
		
		ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&ТекстВыборкиИзмерений", ТекстВыборкиИзмерений);
		
	ИначеЕсли ОбщегоНазначения.ЭтоРегистр(МетаданныеОбъекта) Тогда
		
		Если ОтложенноеОбновлениеЗавершеноУспешно Тогда
			ТекстЗапроса =
			"ВЫБРАТЬ
			|	&ПустоеЗначение КАК Регистратор
			|ПОМЕСТИТЬ #ИмяВременнойТаблицы
			|ГДЕ
			|	ЛОЖЬ";
			
			Запрос.УстановитьПараметр("ПустоеЗначение", МетаданныеОбъекта.СтандартныеРеквизиты.Регистратор.Тип.ПривестиЗначение()); 
			
		Иначе
			ТекстЗапроса =
			"ВЫБРАТЬ РАЗЛИЧНЫЕ
			|	ТаблицаИзменений.Регистратор КАК Регистратор
			|ПОМЕСТИТЬ #ИмяВременнойТаблицы
			|ИЗ
			|	#ТаблицаИзменений КАК ТаблицаИзменений
			|ГДЕ
			|	&УсловиеОтбораУзла
			|
			|ИНДЕКСИРОВАТЬ ПО
			|	Регистратор";
		КонецЕсли;
		
		
		Если ВРег(ИмяИзмеренияДляОтбора) <> ВРег("Регистратор") Тогда
			ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "Регистратор", ИмяИзмеренияДляОтбора);
		КонецЕсли;
	Иначе
		ТекстИсключения = НСтр("ru = 'Для этого типа метаданных не поддерживается проверка в функции %1.'");
		ТекстИсключения = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(ТекстИсключения, "ОбновлениеИнформационнойБазы.СоздатьВременнуюТаблицуЗаблокированныхДляЧтенияИИзмененияДанных");
		ВызватьИсключение ТекстИсключения;
	КонецЕсли;
	
	Если Не ОтложенноеОбновлениеЗавершеноУспешно Тогда
		
		Если Очередь = Неопределено Тогда
			УсловиеОтбораУзла = "	ТаблицаИзменений.Узел ССЫЛКА ПланОбмена.ОбновлениеИнформационнойБазы ";
		Иначе
			УсловиеОтбораУзла = "	ТаблицаИзменений.Узел В (&Узлы) ";
			Запрос.УстановитьПараметр("Узлы", УзлыМеньшейОчереди(Очередь));
		КонецЕсли;	
		ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&УсловиеОтбораУзла", УсловиеОтбораУзла);
	
		ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "#ТаблицаИзменений", ПолноеИмяОбъекта + ".Изменения");
		
	КонецЕсли;
	
	ИмяОбъекта = СтрРазделить(ПолноеИмяОбъекта, ".")[1];
	
	Если ПустаяСтрока(ДополнительныеПараметры.ИмяВременнойТаблицы) Тогда
		ИмяВременнойТаблицы =  "ВТЗаблокировано"+ИмяОбъекта;
	Иначе
		ИмяВременнойТаблицы = ДополнительныеПараметры.ИмяВременнойТаблицы;
	КонецЕсли;
	
	ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "#ИмяВременнойТаблицы", ИмяВременнойТаблицы);
	
	Запрос.Текст = ТекстЗапроса;
	
	РезультатЗапроса = Запрос.Выполнить();
	
	Результат = Новый Структура("ЕстьЗаписиВоВременнойТаблице,ИмяВременнойТаблицы", Ложь, "");
	Результат.ИмяВременнойТаблицы = ИмяВременнойТаблицы;
	Результат.ЕстьЗаписиВоВременнойТаблице = РезультатЗапроса.Выгрузить()[0].Количество <> 0;
			
	Возврат Результат;
	
КонецФункции

// Создает временную таблицу заблокированных ссылок.
//  Имя таблицы: ВТЗаблокировано.
//  Колонки таблицы:
//    * Ссылка.
//
// Параметры:
//  Очередь                 - Число
//                          - Неопределено - очередь обработки, в которой выполняется
//                             текущий обработчик. Если передано Неопределено, то проверяется во всех очередях.
//  ПолныеИменаОбъектов     - Строка
//                          - Массив - полные имена объектов, для которых выполняется проверка,
//                             например Справочник.Номенклатура.
//                             Могут быть переданы объекты ссылочного типа, или регистры, подчиненные регистратору.
//  МенеджерВременныхТаблиц - МенеджерВременныхТаблиц - менеджер, в котором будет создана временная таблица.
//  ДополнительныеПараметры - см. ОбновлениеИнформационнойБазы.ДополнительныеПараметрыВыборкиДанныхДляОбработки
//
// Возвращаемое значение:
//  Структура:
//    * ЕстьЗаписиВоВременнойТаблице - Булево - в создаваемой таблице есть хотя бы одна запись.
//    * ИмяВременнойТаблицы          - Строка - имя созданной временной таблицы.
//
Функция СоздатьВременнуюТаблицуЗаблокированныхДляЧтенияИИзмененияСсылок(Очередь, ПолныеИменаОбъектов, МенеджерВременныхТаблиц, ДополнительныеПараметры = Неопределено) Экспорт
	
	Если ДополнительныеПараметры = Неопределено Тогда
		ДополнительныеПараметры = ДополнительныеПараметрыВыборкиДанныхДляОбработки();
	КонецЕсли;
	
	Запрос = Новый Запрос;
	Запрос.МенеджерВременныхТаблиц = МенеджерВременныхТаблиц;
	
	Если ПолучитьФункциональнуюОпцию("ОтложенноеОбновлениеЗавершеноУспешно") Тогда
		ТекстЗапроса =
		"ВЫБРАТЬ
		|	НЕОПРЕДЕЛЕНО КАК Ссылка
		|ПОМЕСТИТЬ #ИмяВременнойТаблицы
		|ГДЕ
		|	ЛОЖЬ";
	Иначе	
		Если ТипЗнч(ПолныеИменаОбъектов) = Тип("Строка") Тогда
			ПолныеИменаОбъектовМассив = СтрРазделить(ПолныеИменаОбъектов,",",Ложь);
		ИначеЕсли ТипЗнч(ПолныеИменаОбъектов) = Тип("Массив") Тогда 
			ПолныеИменаОбъектовМассив = ПолныеИменаОбъектов;
		Иначе
			ПолныеИменаОбъектовМассив = Новый Массив;
			ПолныеИменаОбъектовМассив.Добавить(ПолныеИменаОбъектов);
		КонецЕсли;
		
		МассивТекстовЗапросов = Новый Массив;
		
		ЕстьРегистры = Ложь;
		
		Для Каждого ОбрабатываемыйТип Из ПолныеИменаОбъектовМассив Цикл
			
			Если ТипЗнч(ОбрабатываемыйТип) = Тип("ОбъектМетаданных") Тогда
				МетаданныеОбъекта = ОбрабатываемыйТип;
				ПолноеИмяОбъекта  = ОбрабатываемыйТип.ПолноеИмя();
			Иначе
				МетаданныеОбъекта = ОбщегоНазначения.ОбъектМетаданныхПоПолномуИмени(ОбрабатываемыйТип);
				ПолноеИмяОбъекта  = ОбрабатываемыйТип;
			КонецЕсли;
			
			МетаданныеОбъекта = ОбщегоНазначения.ОбъектМетаданныхПоПолномуИмени(ПолноеИмяОбъекта);
			
			Если ОбщегоНазначения.ЭтоОбъектСсылочногоТипа(МетаданныеОбъекта) Тогда
				Если МассивТекстовЗапросов.Количество() = 0 Тогда
					ТекстЗапроса =
					"ВЫБРАТЬ
					|	ТаблицаИзменений.Ссылка КАК Ссылка
					|ПОМЕСТИТЬ #ИмяВременнойТаблицыПервогоЗапроса
					|ИЗ
					|	#ТаблицаИзменений КАК ТаблицаИзменений
					|ГДЕ
					|	&УсловиеОтбораУзла";
				Иначе
					ТекстЗапроса =
					"ВЫБРАТЬ
					|	ТаблицаИзменений.Ссылка КАК Ссылка
					|ИЗ
					|	#ТаблицаИзменений КАК ТаблицаИзменений
					|ГДЕ
					|	&УсловиеОтбораУзла";	
				КонецЕсли;
			ИначеЕсли ОбщегоНазначения.ЭтоРегистр(МетаданныеОбъекта) Тогда
				Если МассивТекстовЗапросов.Количество() = 0 Тогда
					ТекстЗапроса =
					"ВЫБРАТЬ
					|	ТаблицаИзменений.Регистратор КАК Ссылка
					|ПОМЕСТИТЬ #ИмяВременнойТаблицыПервогоЗапроса
					|ИЗ
					|	#ТаблицаИзменений КАК ТаблицаИзменений
					|ГДЕ
					|	&УсловиеОтбораУзла";
				Иначе
					ТекстЗапроса =
					"ВЫБРАТЬ
					|	ТаблицаИзменений.Регистратор КАК Ссылка
					|ИЗ
					|	#ТаблицаИзменений КАК ТаблицаИзменений
					|ГДЕ
					|	&УсловиеОтбораУзла";
				КонецЕсли;
				
				ЕстьРегистры = Истина;
				
			Иначе
				ТекстИсключения = НСтр("ru = 'Для типа метаданных ""%1"" не поддерживается проверка в функции %2'");
				ТекстИсключения = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(ТекстИсключения,
					Строка(МетаданныеОбъекта), "ОбновлениеИнформационнойБазы.СоздатьВременнуюТаблицуЗаблокированныхДляЧтенияИИзмененияСсылок");
				ВызватьИсключение ТекстИсключения;
			КонецЕсли;
		
			ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "#ТаблицаИзменений", ПолноеИмяОбъекта + ".Изменения");
			
			МассивТекстовЗапросов.Добавить(ТекстЗапроса);
		КонецЦикла;
		
		Соединитель = "
		|
		|ОБЪЕДИНИТЬ ВСЕ
		|";
		
		ТекстЗапроса = СтрСоединить(МассивТекстовЗапросов, Соединитель); 
		
		Если ЕстьРегистры И МассивТекстовЗапросов.Количество() > 1 Тогда
			ШаблонЗапроса =
			"ВЫБРАТЬ РАЗЛИЧНЫЕ
			|	ВложенныйЗапрос.Ссылка КАК Ссылка
			|ПОМЕСТИТЬ #ИмяВременнойТаблицы
			|ИЗ
			|	#ТекстЗапроса КАК ВложенныйЗапрос
			|
			|ИНДЕКСИРОВАТЬ ПО
			|	Ссылка";
			ТекстЗапроса = СтрЗаменить(ШаблонЗапроса, "#ТекстЗапроса", "(" + ТекстЗапроса + ")");
			ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "ПОМЕСТИТЬ #ИмяВременнойТаблицыПервогоЗапроса", "");
		Иначе
			ТекстЗапроса = ТекстЗапроса + "
			|
			|ИНДЕКСИРОВАТЬ ПО
			|	Ссылка";
			ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "#ИмяВременнойТаблицыПервогоЗапроса", "#ИмяВременнойТаблицы");
		КонецЕсли;
		
		Если Очередь = Неопределено Тогда
			УсловиеОтбораУзла = "	ТаблицаИзменений.Узел ССЫЛКА ПланОбмена.ОбновлениеИнформационнойБазы ";
		Иначе
			УсловиеОтбораУзла = "	ТаблицаИзменений.Узел В (&Узлы) ";
			Запрос.УстановитьПараметр("Узлы", УзлыМеньшейОчереди(Очередь));
		КонецЕсли;	
		ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&УсловиеОтбораУзла", УсловиеОтбораУзла);
	КонецЕсли;	
	
	Если ПустаяСтрока(ДополнительныеПараметры.ИмяВременнойТаблицы) Тогда
		ИмяВременнойТаблицы =  "ВТЗаблокировано";
	Иначе
		ИмяВременнойТаблицы = ДополнительныеПараметры.ИмяВременнойТаблицы;
	КонецЕсли;
	ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "#ИмяВременнойТаблицы", ИмяВременнойТаблицы);
	
	Запрос.Текст = ТекстЗапроса;
	РезультатЗапроса = Запрос.Выполнить();
	
	Результат = Новый Структура("ЕстьЗаписиВоВременнойТаблице, ИмяВременнойТаблицы", Ложь, "");
	Результат.ИмяВременнойТаблицы = ИмяВременнойТаблицы;
	Результат.ЕстьЗаписиВоВременнойТаблице = РезультатЗапроса.Выгрузить()[0].Количество <> 0;
	
	Возврат Результат;
	
КонецФункции

// Создает временную таблицу измерений регистров, подчиненных регистраторам, по которым еще есть не обработанные регистраторы.
//  Алгоритм вычисления:
//  - берутся заблокированные регистраторы;
//  - по ним соединяется с основной таблицей регистра;
//  - из основной таблицы получаются значения изменений;
//  - делается группировка.
//  Имя таблицы: ВТЗаблокированы<ИмяОбъекта>, например ВТЗаблокированоТоварыНаСкладах.
//  Колонки таблицы соответствуют переданным измерениям.
//
// Параметры:
//  Очередь                 - Число
//                          - Неопределено - очередь обработки, в которой выполняется
//                             текущий обработчик. Если передано Неопределено,
//                             то проверяется во всех очередях.
//  ПолноеИмяРегистра       - Строка - имя регистра, движения по которому нужно переформировать.
//                             Например, РегистрНакопления.ТоварыНаСкладах
//  Измерения               - Строка
//                          - Массив - имена измерений, по которым нужно проверить блокировку,
//                             перечисленные через запятую, или массив имен.
//  МенеджерВременныхТаблиц - МенеджерВременныхТаблиц - менеджер, в котором будет создана временная таблица.
//  ДополнительныеПараметры - см. ОбновлениеИнформационнойБазы.ДополнительныеПараметрыВыборкиДанныхДляОбработки
//
// Возвращаемое значение:
//  Структура:
//   * ЕстьЗаписиВоВременнойТаблице - Булево - в создаваемой таблице есть хотя бы одна запись.
//   * ИмяВременнойТаблицы          - Строка - имя созданной временной таблицы.
//
Функция СоздатьВременнуюТаблицуЗначенийЗаблокированныхИзмерений(Очередь, ПолноеИмяРегистра, Измерения, МенеджерВременныхТаблиц, ДополнительныеПараметры = Неопределено) Экспорт
	
	Если ДополнительныеПараметры = Неопределено Тогда
		ДополнительныеПараметры = ДополнительныеПараметрыВыборкиДанныхДляОбработки();
	КонецЕсли;
	
	Запрос = Новый Запрос;
	Запрос.МенеджерВременныхТаблиц = МенеджерВременныхТаблиц;
	
	Если ТипЗнч(Измерения) = Тип("Строка") Тогда
		ИзмеренияМассив = СтрРазделить(Измерения, ",", Ложь);
	Иначе
		ИзмеренияМассив = Измерения;
	КонецЕсли;
	
	МетаданныеОбъекта = ОбщегоНазначения.ОбъектМетаданныхПоПолномуИмени(ПолноеИмяРегистра);
	ИменаИзмерений = Новый Массив;
	ПсевдонимыИзмерений = Новый Массив;
	
	Если ПолучитьФункциональнуюОпцию("ОтложенноеОбновлениеЗавершеноУспешно") Тогда
		ТекстЗапроса =
		"ВЫБРАТЬ
		|	&ЗначенияИзмерений
		|ПОМЕСТИТЬ #ИмяВременнойТаблицы
		|ГДЕ
		|	ЛОЖЬ";
		
		Для Каждого ИзмерениеСтр Из ИзмеренияМассив Цикл
			Измерение = МетаданныеОбъекта.Измерения.Найти(ИзмерениеСтр);
			ИменаИзмерений.Добавить("&ПустоеЗначениеИзмерения" + Измерение.Имя);
			ПсевдонимыИзмерений.Добавить(Измерение.Имя);
			Запрос.УстановитьПараметр("ПустоеЗначениеИзмерения" + Измерение.Имя, Измерение.Тип.ПривестиЗначение()); 
		КонецЦикла;
	Иначе
		
		ТекстЗапроса =
		"ВЫБРАТЬ РАЗЛИЧНЫЕ
		|	&ЗначенияИзмерений
		|ПОМЕСТИТЬ #ИмяВременнойТаблицы
		|ИЗ
		|	#ТаблицаИзменений КАК ТаблицаИзменений
		|		ВНУТРЕННЕЕ СОЕДИНЕНИЕ #ТаблицаРегистра КАК ТаблицаРегистра
		|		ПО ТаблицаИзменений.Регистратор = ТаблицаРегистра.Регистратор
		|ГДЕ
		|	&УсловиеОтбораУзла
		|ИНДЕКСИРОВАТЬ ПО
		|	&ИндексируемыеИзмерения";
		
		Для Каждого Измерение Из ИзмеренияМассив Цикл
			ИменаИзмерений.Добавить("ТаблицаРегистра." + Измерение);
			ПсевдонимыИзмерений.Добавить(Измерение);
		КонецЦикла;
		
		ТекстИндексируемыхИзмерений = УпорядочиванияДляЗапроса(ПсевдонимыИзмерений);
		ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&ИндексируемыеИзмерения", ТекстИндексируемыхИзмерений);
		ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "#ТаблицаИзменений", ПолноеИмяРегистра + ".Изменения");
		ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "#ТаблицаРегистра", ПолноеИмяРегистра);
		
		Если Очередь = Неопределено Тогда
			УсловиеОтбораУзла = "	ТаблицаИзменений.Узел ССЫЛКА ПланОбмена.ОбновлениеИнформационнойБазы ";
		Иначе
			УсловиеОтбораУзла = "	ТаблицаИзменений.Узел В (&Узлы) ";
			Запрос.УстановитьПараметр("Узлы", УзлыМеньшейОчереди(Очередь));
		КонецЕсли;	
		
		ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&УсловиеОтбораУзла", УсловиеОтбораУзла);
	КонецЕсли;
	
	ИмяОбъекта = СтрРазделить(ПолноеИмяРегистра, ".")[1];
	Если ПустаяСтрока(ДополнительныеПараметры.ИмяВременнойТаблицы) Тогда
		ИмяВременнойТаблицы =  "ВТЗаблокировано" + ИмяОбъекта;
	Иначе
		ИмяВременнойТаблицы = ДополнительныеПараметры.ИмяВременнойТаблицы;
	КонецЕсли;
	ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "#ИмяВременнойТаблицы", ИмяВременнойТаблицы);
	
	ЗначенияИзмерений = ПоляДляЗапроса(ИменаИзмерений, ПсевдонимыИзмерений);
	ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&ЗначенияИзмерений", ЗначенияИзмерений);
	Запрос.Текст = ТекстЗапроса;
	РезультатЗапроса = Запрос.Выполнить();
	
	Результат = Новый Структура("ЕстьЗаписиВоВременнойТаблице,ИмяВременнойТаблицы", Ложь, "");
	Результат.ИмяВременнойТаблицы = ИмяВременнойТаблицы;
	Результат.ЕстьЗаписиВоВременнойТаблице = РезультатЗапроса.Выгрузить()[0].Количество <> 0;
			
	Возврат Результат;
	
КонецФункции

// Функция для проверки объектов при открытии форм и перед записью.
// Может использоваться как функция проверки по умолчанию, если
// достаточно логики - заблокированные объекты зарегистрированы на узлах плана обмена ОбновлениеИнформационнойБазы.
//
// Параметры:
//  МетаданныеИОтбор - см. ОбновлениеИнформационнойБазы.МетаданныеИОтборПоДанным.
//
// Возвращаемое значение:
//  Булево - Истина, если объект обновлен и доступен для изменения.
//
Функция ДанныеОбновленыНаНовуюВерсиюПрограммы(МетаданныеИОтбор) Экспорт
	
	Возврат МожноЧитатьИМенять(Неопределено, МетаданныеИОтбор.Данные,,МетаданныеИОтбор); 
	
КонецФункции

// Выборка данных через ВыбратьИзмеренияНезависимогоРегистраСведенийДляОбработки().
//
// Возвращаемое значение:
//  Строка - константа "ИзмеренияНезависимогоРегистраСведений".
//
Функция СпособВыборкиИзмеренияНезависимогоРегистраСведений() Экспорт
	
	Возврат "ИзмеренияНезависимогоРегистраСведений";
	
КонецФункции

// Выборка данных через ВыбратьРегистраторыРегистраДляОбработки().
//
// Возвращаемое значение:
//  Строка - константа "РегистраторыРегистра".
//
Функция СпособВыборкиРегистраторыРегистра() Экспорт
	
	Возврат "РегистраторыРегистра";
	
КонецФункции

// Выборка данных через ВыбратьСсылкиДляОбработки().
//
// Возвращаемое значение:
//  Строка - константа "Ссылки".
//
Функция СпособВыборкиСсылки() Экспорт
	
	Возврат "Ссылки";
	
КонецФункции

////////////////////////////////////////////////////////////////////////////////
// Прочие процедуры и функции.

// Проверить необходимость обновления информационной базы при смене версии конфигурации.
//
// Возвращаемое значение:
//   Булево - Истина, если требуется обновление.
//
Функция НеобходимоОбновлениеИнформационнойБазы() Экспорт
	
	Возврат ОбновлениеИнформационнойБазыСлужебныйПовтИсп.НеобходимоОбновлениеИнформационнойБазы();
	
КонецФункции

// Возвращает Истина, если в данный момент выполняется обновление ИБ.
//
// Возвращаемое значение:
//   Булево - Истина, если обновление выполняется.
//
Функция ВыполняетсяОбновлениеИнформационнойБазы() Экспорт
	
	Если ОбщегоНазначения.РазделениеВключено()
		И Не ОбщегоНазначения.ДоступноИспользованиеРазделенныхДанных() Тогда
		Возврат НеобходимоОбновлениеИнформационнойБазы();
	КонецЕсли;
	
	Возврат ПараметрыСеанса.ВыполняетсяОбновлениеИБ;
	
КонецФункции

// Возвращает признак завершения отложенного обновления.
//
// Параметры:
//  ИменаПодсистем - Строка - если передано, будет проверяться результат завершения
//                            обновления для переданной подсистемы, а не для всей конфигурации.
//                 - Массив - если требуется проверить завершение обновления сразу нескольких
//                            подсистем.
//
// Возвращаемое значение:
//  Булево - Истина, если обновление завершено.
//
Функция ОтложенноеОбновлениеЗавершено(Знач ИменаПодсистем = Неопределено) Экспорт
	
	Если ПолучитьФункциональнуюОпцию("ОтложенноеОбновлениеЗавершеноУспешно") Тогда
		ЭтоПодчиненныйУзелРИБ = ОбщегоНазначения.ЭтоПодчиненныйУзелРИБ();
		Если Не ЭтоПодчиненныйУзелРИБ Тогда
			Возврат Истина;
		Иначе
			Возврат ПолучитьФункциональнуюОпцию("ОтложенноеОбновлениеВГлавномУзлеЗавершеноУспешно");
		КонецЕсли;
	КонецЕсли;
	
	Если Не ЗначениеЗаполнено(ИменаПодсистем) Тогда
		Возврат Ложь;
	КонецЕсли;
	
	Если ТипЗнч(ИменаПодсистем) = Тип("Строка") Тогда
		ИменаПодсистем = СтрРазделить(ИменаПодсистем, ",");
	КонецЕсли;
	
	Запрос = Новый Запрос;
	Запрос.УстановитьПараметр("Подсистемы", ИменаПодсистем);
	Запрос.УстановитьПараметр("Статус", Перечисления.СтатусыОбработчиковОбновления.Выполнен);
	Запрос.Текст =
		"ВЫБРАТЬ
		|	ОбработчикиОбновления.ИмяОбработчика КАК ИмяОбработчика
		|ИЗ
		|	РегистрСведений.ОбработчикиОбновления КАК ОбработчикиОбновления
		|ГДЕ
		|	ОбработчикиОбновления.ИмяБиблиотеки В (&Подсистемы)
		|	И ОбработчикиОбновления.Статус <> &Статус";
	Возврат Запрос.Выполнить().Выгрузить().Количество() = 0;
	
КонецФункции

// Возвращает Истина, если вызов функции выполняется из обработчика обновления.
// Для любого вида обработчика обновления - монопольного, оперативного и отложенного.
//
// Параметры:
//  РежимВыполненияОбработчика - Строка - Отложенно, Оперативно, Монопольно или комбинация данных
//                               вариантов через запятую. Если указано, то проверяется только
//                               вызов из обработчиков обновления с данным режимом выполнения.
//
// Возвращаемое значение:
//  Булево - Истина, если вызов функции выполняется из обработчика обновления.
//
Функция ЭтоВызовИзОбработчикаОбновления(РежимВыполненияОбработчика = "") Экспорт
	
	РежимВыполнения = ПараметрыСеанса.ПараметрыОбработчикаОбновления.РежимВыполнения;
	Если Не ЗначениеЗаполнено(РежимВыполнения) Тогда
		Возврат Ложь;
	КонецЕсли;
	
	Если Не ЗначениеЗаполнено(РежимВыполненияОбработчика) Тогда
		Возврат Истина;
	КонецЕсли;
	
	Возврат (СтрНайти(РежимВыполненияОбработчика, РежимВыполнения) > 0);
	
КонецФункции

// Возвращает пустую таблицу обработчиков обновления и первоначального заполнения ИБ. 
//
// Возвращаемое значение:
//   ТаблицаЗначений   - таблица с колонками:
//    1) Для всех типов обработчиков обновления:
//
//     * НачальноеЗаполнение - Булево - если Истина, то обработчик должен срабатывать при запуске на "пустой" базе.
//     * Версия              - Строка - например, "2.1.3.39". Номер версии конфигурации, при переходе
//                                      на которую должна быть выполнена процедура-обработчик обновления.
//                                      Если указана пустая строка, то это обработчик только для начального заполнения
//                                      (должно быть указано свойство НачальноеЗаполнение).
//     * Процедура           - Строка - полное имя процедуры-обработчика обновления/начального заполнения. 
//                                      Например, "ОбновлениеИнформационнойБазыУПП.ЗаполнитьНовыйРеквизит"
//                                      Обязательно должна быть экспортной.
//     * РежимВыполнения     - Строка - режим выполнения обработчика обновления. Допустимые значения:
//                                      Монопольно, Отложенно, Оперативно. Если значение не заполнено, обработчик
//                                      считается монопольным.
//
//    2) Для обработчиков обновления в модели сервиса:
//
//     * ОбщиеДанные         - Булево - если Истина, то обработчик должен срабатывать до
//                                      выполнения любых обработчиков, использующих разделенные данные.
//                                      Допустимо указывать только для обработчиков с режимом выполнения Монопольно и Оперативно.
//                                      Если указать значение Истина для обработчика с режимом
//                                      выполнения Отложенно, будет выдано исключение.
//     * УправлениеОбработчиками - Булево - если Истина, то обработчик должен иметь параметр типа Структура, в котором
//                                          есть свойство РазделенныеОбработчики - таблица значений со структурой,
//                                          возвращаемой этой функцией.
//                                      При этом колонка Версия игнорируется. В случае необходимости выполнения
//                                      разделенного обработчика в данную таблицу необходимо добавить строку с
//                                      описанием процедуры обработчика.
//                                      Имеет смысл только для обязательных (Версия = *) обработчиков обновления 
//                                      с установленным флагом ОбщиеДанные.
//
//    3) Для отложенных обработчиков обновления:
//
//     * Комментарий         - Строка - описание действий, выполняемых обработчиком обновления.
//     * Идентификатор       - УникальныйИдентификатор - необходимо заполнять для обработчиков отложенного обновления,
//                                                 для остальных заполнение не требуется. Требуется для идентификации
//                                                 обработчика в случае его переименования.
//     
//     * БлокируемыеОбъекты  - Строка - необходимо заполнять для обработчиков отложенного обновления,
//                                      для остальных заполнение не требуется. Полные имена объектов через запятую, 
//                                      которые следует блокировать от изменения до завершения процедуры обработки данных.
//                                      Если заполнено, то также требуется заполнить и свойство ПроцедураПроверки.
//     * НовыеОбъекты        - Строка - необходимо заполнять для обработчиков отложенного обновления,
//                                      для остальных заполнение не требуется. Полные имена объектов через запятую, 
//                                      которые создаются при работе обработчика обновления.
//     * ПроцедураПроверки   - Строка - необходимо заполнять для обработчиков отложенного обновления,
//                                      для остальных заполнение не требуется. Имя функции, которая для переданного объекта 
//                                      определяет, завершена ли для него процедура обработки данных. 
//                                      Если переданный объект обработан, то следует вернуть значение Истина. 
//                                      Вызывается из процедуры ОбновлениеИнформационнойБазы.ПроверитьОбъектОбработан.
//                                      Параметры, передаваемые в функцию:
//                                         Параметры - см. ОбновлениеИнформационнойБазы.МетаданныеИОтборПоДанным.
//
//    4) Для обработчиков обновления в библиотеках (конфигурации) с параллельным режимом выполнения отложенных обработчиков:
//
//     * ПроцедураЗаполненияДанныхОбновления - Строка - указывается процедура, которая регистрирует данные,
//                                      подлежащие обновлению данным обработчиком.
//     * ЗапускатьТолькоВГлавномУзле  - Булево - только для обработчиков отложенного обновления с режимом выполнения Параллельно.
//                                      Указать Истина, если обработчик обновления должен выполняться только в главном
//                                      узле РИБ.
//     * ЗапускатьИВПодчиненномУзлеРИБСФильтрами - Булево - только для обработчиков отложенного обновления с режимом
//                                      выполнения Параллельно.
//                                      Указать Истина, если обработчик обновления должен также выполняться в
//                                      подчиненном узле РИБ с фильтрами.
//     * ЧитаемыеОбъекты              - Строка - объекты, которые обработчик обновления будет читать при обработке данных.
//     * ИзменяемыеОбъекты            - Строка - объекты, которые обработчик обновления будет изменять при обработке данных.
//     * ПриоритетыВыполнения         - ТаблицаЗначений - таблица приоритетов выполнения между отложенными обработчиками,
//                                      изменяющими или читающими одни и те же данные. Подробнее см. в комментарии
//                                      к функции ОбновлениеИнформационнойБазы.ПриоритетыВыполненияОбработчика.
//     * Многопоточный                - Булево - Истина, если обработчик адаптирован для обработки данных в несколько потоков.
//     * Порядок  - ПеречислениеСсылка.ПорядокОбработчиковОбновления
//    
//    5) Для оперативных и монопольных обработчиков начального заполнения:
//     * НеВыполнятьПриПереходеСДругойПрограммы - Булево - при переходе с другой программы обработчики начального
//                                          заполнения новых подсистем выполняются автоматически. Если установить
//                                          Истина, такой обработчик не будет выполнен.
//
//    6) Для внутреннего использования:
//
//     * ВыполнятьВГруппеОбязательных - Булево - следует указывать, если обработчик требуется
//                                      выполнять в одной группе с обработчиками на версии "*".
//                                      При этом возможно менять порядок выполнения обработчика
//                                      относительно других путем изменения приоритета.
//     * Приоритет           - Число  - для внутреннего использования.
//
//    7) Устарели, используются для обратной совместимости (для новых обработчиков не указывать):
//
//     * МонопольныйРежим    - Неопределено
//                           - Булево - если указано Неопределено, то обработчик 
//                                      должен безусловно выполняться в монопольном режиме.
//                                      Для обработчиков перехода на конкретную версию (версия <> *):
//                                        Ложь   - обработчик не требует монопольного режима для выполнения.
//                                        Истина - обработчик требует монопольного режима для выполнения.
//                                      Для обязательных обработчиков обновления (Версия = "*"):
//                                        Ложь   - обработчик не требует монопольного режима.
//                                        Истина - обработчик может требовать монопольного режима для выполнения.
//                                                 В такие обработчики передается параметр типа структура
//                                                 со свойством МонопольныйРежим (типа Булево).
//                                                 При запуске обработчика в монопольном режиме передается
//                                                 значение Истина. В этом случае обработчик должен выполнить
//                                                 требуемые действия по обновлению. Изменение параметра
//                                                 в теле обработчика игнорируется.
//                                                 При запуске обработчика в немонопольном режиме передается
//                                                 значение Ложь. В этом случае обработчик не должен вносить никакие
//                                                 изменения в ИБ.
//                                                 Если в результате анализа выясняется, что обработчику требуется
//                                                 изменить данные ИБ, следует установить значение параметра в Истина
//                                                 и прекратить выполнение обработчика.
//                                                 В этом случае оперативное (немонопольное) обновление ИБ будет
//                                                 отменено и будет выдана ошибка с требованием выполнить обновление в
//                                                 монопольном режиме.
//
Функция НоваяТаблицаОбработчиковОбновления() Экспорт
	
	Обработчики = Новый ТаблицаЗначений;
	// Общие свойства.
	Обработчики.Колонки.Добавить("НачальноеЗаполнение", Новый ОписаниеТипов("Булево"));
	Обработчики.Колонки.Добавить("Версия",    Новый ОписаниеТипов("Строка", , Новый КвалификаторыСтроки(0)));
	Обработчики.Колонки.Добавить("Процедура", Новый ОписаниеТипов("Строка", , Новый КвалификаторыСтроки(0)));
	Обработчики.Колонки.Добавить("РежимВыполнения", Новый ОписаниеТипов("Строка"));
	// Для библиотек.
	Обработчики.Колонки.Добавить("ВыполнятьВГруппеОбязательных", Новый ОписаниеТипов("Булево"));
	Обработчики.Колонки.Добавить("Приоритет", Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(2)));
	// Для модели сервиса.
	Обработчики.Колонки.Добавить("ОбщиеДанные",             Новый ОписаниеТипов("Булево"));
	Обработчики.Колонки.Добавить("УправлениеОбработчиками", Новый ОписаниеТипов("Булево"));
	// Для отложенных обработчиков обновления.
	Обработчики.Колонки.Добавить("Комментарий", Новый ОписаниеТипов("Строка", , Новый КвалификаторыСтроки(0)));
	Обработчики.Колонки.Добавить("Идентификатор", Новый ОписаниеТипов("УникальныйИдентификатор"));
	Обработчики.Колонки.Добавить("ПроцедураПроверки", Новый ОписаниеТипов("Строка"));
	Обработчики.Колонки.Добавить("БлокируемыеОбъекты", Новый ОписаниеТипов("Строка"));
	Обработчики.Колонки.Добавить("НовыеОбъекты", Новый ОписаниеТипов("Строка"));
	// Для параллельного режима отложенного обновления.
	Обработчики.Колонки.Добавить("ПроцедураЗаполненияДанныхОбновления", Новый ОписаниеТипов("Строка", , Новый КвалификаторыСтроки(0)));
	Обработчики.Колонки.Добавить("ОчередьОтложеннойОбработки",  Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(4)));
	Обработчики.Колонки.Добавить("ЗапускатьТолькоВГлавномУзле",  Новый ОписаниеТипов("Булево"));
	Обработчики.Колонки.Добавить("ЗапускатьИВПодчиненномУзлеРИБСФильтрами",  Новый ОписаниеТипов("Булево"));
	Обработчики.Колонки.Добавить("ЧитаемыеОбъекты", Новый ОписаниеТипов("Строка", , Новый КвалификаторыСтроки(0)));
	Обработчики.Колонки.Добавить("ИзменяемыеОбъекты", Новый ОписаниеТипов("Строка", , Новый КвалификаторыСтроки(0)));
	Обработчики.Колонки.Добавить("ПриоритетыВыполнения");
	Обработчики.Колонки.Добавить("Многопоточный", Новый ОписаниеТипов("Булево"));
	Обработчики.Колонки.Добавить("Порядок", Новый ОписаниеТипов("ПеречислениеСсылка.ПорядокОбработчиковОбновления"));
	// Для обработчиков начального заполнения.
	Обработчики.Колонки.Добавить("НеВыполнятьПриПереходеСДругойПрограммы", Новый ОписаниеТипов("Булево"));
	
	// Устарело. Обратная совместимость с редакцией "2.2".
	Обработчики.Колонки.Добавить("Опциональный");
	Обработчики.Колонки.Добавить("МонопольныйРежим");
	
	Возврат Обработчики;
	
КонецФункции

// Возвращает пустую таблицу приоритетов выполнения между отложенными обработчиками,
// изменяющими или читающими одни и те же данные. Для использования в описании обработчиков обновления.
//
// Возвращаемое значение:
//  ТаблицаЗначений:
//    * Порядок       - Строка - порядок выполнения обработчика относительно указанной процедуры.
//                               Допустимые варианты: "До", "После", "Любой".
//    * Идентификатор - УникальныйИдентификатор - идентификатор процедуры, с которой настраивается взаимосвязь.
//    * Процедура     - Строка - полное имя процедуры, относительно которой выполняется обработчик.
//
// Пример:
//  Приоритет = ПриоритетыВыполненияОбработчика().Добавить();
//  Приоритет.Порядок = "До"; // порядок выполнения обработчика относительно процедуры ниже.
//  Приоритет.Процедура = "Документ.ЗаказПокупателя.ОбновитьДанныеДляПереходаНаНовуюВерсию";
//
Функция ПриоритетыВыполненияОбработчика() Экспорт
	
	Приоритеты = Новый ТаблицаЗначений;
	Приоритеты.Колонки.Добавить("Порядок", Новый ОписаниеТипов("Строка", , Новый КвалификаторыСтроки(0)));
	Приоритеты.Колонки.Добавить("Идентификатор");
	Приоритеты.Колонки.Добавить("Процедура", Новый ОписаниеТипов("Строка", , Новый КвалификаторыСтроки(0)));
	
	Возврат Приоритеты;
	
КонецФункции

// Выполнить обработчики обновления из списка ОбработчикиОбновления 
// для библиотеки ИдентификаторБиблиотеки до версии ВерсияМетаданныхИБ.
//
// Параметры:
//   ИдентификаторБиблиотеки   - Строка       - имя конфигурации или идентификатор библиотеки.
//   ВерсияМетаданныхИБ        - Строка       - версия метаданных, до которой необходимо выполнить обновление.
//   ОбработчикиОбновления     - Соответствие - список обработчиков обновления.
//   ХодВыполненияОбработчиков - Структура:
//       * ВсегоОбработчиков     - Строка - общее количество выполняемых обработчиков.
//       * ВыполненоОбработчиков - Булево - количество выполненных обработчиков.
//   ОперативноеОбновление     - Булево       - Истина, если обновление оперативное.
//
// Возвращаемое значение:
//   ДеревоЗначений   - выполненные обработчики обновления.
//
Функция ВыполнитьИтерациюОбновления(Знач ИдентификаторБиблиотеки, Знач ВерсияМетаданныхИБ, 
	Знач ОбработчикиОбновления, Знач ХодВыполненияОбработчиков, Знач ОперативноеОбновление = Ложь) Экспорт
	
	ИтерацияОбновления = ОбновлениеИнформационнойБазыСлужебный.ИтерацияОбновления(ИдентификаторБиблиотеки, 
		ВерсияМетаданныхИБ, ОбработчикиОбновления);
		
	Параметры = Новый Структура;
	Параметры.Вставить("ХодВыполненияОбработчиков", ХодВыполненияОбработчиков);
	Параметры.Вставить("ОперативноеОбновление", ОперативноеОбновление);
	Параметры.Вставить("ВФоне", Ложь);
	
	Возврат ОбновлениеИнформационнойБазыСлужебный.ВыполнитьИтерациюОбновления(ИтерацияОбновления, Параметры);
	
КонецФункции

// Выполнить неинтерактивное обновление данных ИБ.
// Для вызова через внешнее соединение.
// При вызове метода с подключенными расширениями, модифицирующими роли конфигурации, будет вызвано исключение.
// Важно: Перед вызовом метода необходимо запустить удаление устаревших патчей,
// см. функцию ОбновлениеКонфигурации.ИсправленияИзменены().
// 
// Для использования в других библиотеках и конфигурациях.
//
// Параметры:
//  ВыполнятьОтложенныеОбработчики - Булево - если Истина, отложенное обновление будет выполнено
//    в основном цикле обновления. Только для клиент-серверного режима работы.
//
// Возвращаемое значение:
//  Строка -  признак выполнения обработчиков обновления:
//           "Успешно", "НеТребуется", "ОшибкаУстановкиМонопольногоРежима".
//
Функция ВыполнитьОбновлениеИнформационнойБазы(ВыполнятьОтложенныеОбработчики = Ложь) Экспорт
	
	Если ОбщегоНазначения.ПодсистемаСуществует("ТехнологияСервиса.БазоваяФункциональность") Тогда
		МодульТехнологияСервиса = ОбщегоНазначения.ОбщийМодуль("ТехнологияСервиса");
		ВерсияБиблиотекиБТС = МодульТехнологияСервиса.ВерсияБиблиотеки();
		ЕстьПередОбновлениемИнформационнойБазы = ОбновлениеИнформационнойБазыСлужебный.ВесВерсии("2.0.0.0") < ОбновлениеИнформационнойБазыСлужебный.ВесВерсии(ВерсияБиблиотекиБТС);
		Если ЕстьПередОбновлениемИнформационнойБазы Тогда
			Успешно = МодульТехнологияСервиса.ПередОбновлениемИнформационнойБазы(ВыполнятьОтложенныеОбработчики);
			Если Успешно Тогда
				Возврат "Успешно";
			КонецЕсли;
		КонецЕсли;
	КонецЕсли;
	
	ДатаНачала = ТекущаяДатаСеанса();
	Результат = ОбновлениеИнформационнойБазыСлужебныйВызовСервера.ВыполнитьОбновлениеИнформационнойБазы(,,
		ВыполнятьОтложенныеОбработчики);
	ДатаОкончания = ТекущаяДатаСеанса();
	ОбновлениеИнформационнойБазыСлужебный.ЗаписатьВремяВыполненияОбновления(ДатаНачала, ДатаОкончания);
	
	Возврат Результат;
	
КонецФункции

// Возвращает таблицу с версиями подсистем конфигурации.
// Для пакетной выгрузки/загрузки сведений о версиях подсистем.
//
// Возвращаемое значение:
//   ТаблицаЗначений:
//     * ИмяПодсистемы - Строка - имя подсистемы.
//     * Версия        - Строка - версия подсистемы.
//
Функция ВерсииПодсистем() Экспорт
	
	СтандартнаяОбработка = Истина;
	Если ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.РаботаВМоделиСервиса.ОбновлениеВерсииИБВМоделиСервиса") Тогда
		МодульОбновлениеИнформационнойБазыСлужебныйВМоделиСервиса = ОбщегоНазначения.ОбщийМодуль("ОбновлениеИнформационнойБазыСлужебныйВМоделиСервиса");
		ВерсииПодсистемОбластей = МодульОбновлениеИнформационнойБазыСлужебныйВМоделиСервиса.ВерсииПодсистем(СтандартнаяОбработка);
		Если Не СтандартнаяОбработка Тогда
			Возврат ВерсииПодсистемОбластей;
		КонецЕсли;
	КонецЕсли;
	
	Запрос = Новый Запрос;
	Запрос.Текст =
	"ВЫБРАТЬ
	|	ВерсииПодсистем.ИмяПодсистемы КАК ИмяПодсистемы,
	|	ВерсииПодсистем.Версия КАК Версия
	|ИЗ
	|	РегистрСведений.ВерсииПодсистем КАК ВерсииПодсистем";
	
	Возврат Запрос.Выполнить().Выгрузить();

КонецФункции 

// Устанавливает версии всех подсистем.
// Для пакетной выгрузки/загрузки сведений о версиях подсистем.
//
// Параметры:
//   ВерсииПодсистем - ТаблицаЗначений:
//     * ИмяПодсистемы - Строка - имя подсистемы.
//     * Версия        - Строка - версия подсистемы.
//
Процедура УстановитьВерсииПодсистем(ВерсииПодсистем) Экспорт
	
	СтандартнаяОбработка = Истина;
	Если ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.РаботаВМоделиСервиса.ОбновлениеВерсииИБВМоделиСервиса") Тогда
		МодульОбновлениеИнформационнойБазыСлужебныйВМоделиСервиса = ОбщегоНазначения.ОбщийМодуль("ОбновлениеИнформационнойБазыСлужебныйВМоделиСервиса");
		МодульОбновлениеИнформационнойБазыСлужебныйВМоделиСервиса.ПриУстановкеВерсийПодсистем(ВерсииПодсистем, СтандартнаяОбработка);
	КонецЕсли;
	
	Если Не СтандартнаяОбработка Тогда
		Возврат;
	КонецЕсли;
	
	НаборЗаписей = РегистрыСведений.ВерсииПодсистем.СоздатьНаборЗаписей();
	
	Для Каждого Версия Из ВерсииПодсистем Цикл
		НоваяЗапись = НаборЗаписей.Добавить();
		НоваяЗапись.ИмяПодсистемы = Версия.ИмяПодсистемы;
		НоваяЗапись.Версия = Версия.Версия;
		НоваяЗапись.ЭтоОсновнаяКонфигурация = (Версия.ИмяПодсистемы = Метаданные.Имя);
	КонецЦикла;
	
	НаборЗаписей.Записать();

КонецПроцедуры

// Получает версию конфигурации или родительской конфигурации (библиотеки),
// которая хранится в информационной базе.
//
// Параметры:
//  ИдентификаторБиблиотеки   - Строка - имя конфигурации или идентификатор библиотеки.
//
// Возвращаемое значение:
//   Строка   - версия.
//
// Пример:
//   ВерсияКонфигурацииИБ = ОбновлениеИнформационнойБазы.ВерсияИБ(Метаданные.Имя);
//
Функция ВерсияИБ(Знач ИдентификаторБиблиотеки) Экспорт
	
	Возврат ОбновлениеИнформационнойБазыСлужебный.ВерсияИБ(ИдентификаторБиблиотеки);
	
КонецФункции

// Записывает в информационную базу версию конфигурации или родительской конфигурации (библиотеки).
//
// Параметры:
//  ИдентификаторБиблиотеки - Строка - имя конфигурации или родительской конфигурации (библиотеки).
//  НомерВерсии             - Строка - номер версии.
//  ЭтоОсновнаяКонфигурация - Булево - признак, что ИдентификаторБиблиотеки соответствует имени конфигурации.
//
Процедура УстановитьВерсиюИБ(Знач ИдентификаторБиблиотеки, Знач НомерВерсии, Знач ЭтоОсновнаяКонфигурация) Экспорт
	
	ОбновлениеИнформационнойБазыСлужебный.УстановитьВерсиюИБ(ИдентификаторБиблиотеки, НомерВерсии, ЭтоОсновнаяКонфигурация);
	
КонецПроцедуры

// Выполняет регистрацию новой подсистемы в регистре сведений ВерсииПодсистем.
// Необходима, например, в тех случаях, когда новая подсистема создается на
// основе уже существующих метаданных и не требуется выполнять обработчики начального заполнения.
// Если подсистема уже зарегистрирована, то повторная регистрация не выполняется.
// Вызывать данный метод следует из процедуры ПередОбновлениемИнформационнойБазы общего
// модуля ОбновлениеИнформационнойБазыПереопределяемый.
//
// Параметры:
//  ИмяПодсистемы - Строка - имя подсистемы в том виде, в котором оно задано в общем модуле
//                           ОбновлениеИнформационнойБазыХХХ.
//                           Например - "СтандартныеПодсистемы".
//  НомерВерсии   - Строка - полный номер версии, на которую необходимо зарегистрировать подсистему.
//                           Если не указан, то регистрируется на версию "0.0.0.1". Следует указывать,
//                           если нужно, чтобы выполнились не все обработчики, а только последние.
//
Процедура ЗарегистрироватьНовуюПодсистему(ИмяПодсистемы, НомерВерсии = "") Экспорт
	
	СтандартнаяОбработка = Истина;
	Если ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.РаботаВМоделиСервиса.ОбновлениеВерсииИБВМоделиСервиса") Тогда
		МодульОбновлениеИнформационнойБазыСлужебныйВМоделиСервиса = ОбщегоНазначения.ОбщийМодуль("ОбновлениеИнформационнойБазыСлужебныйВМоделиСервиса");
		МодульОбновлениеИнформационнойБазыСлужебныйВМоделиСервиса.ЗарегистрироватьНовуюПодсистему(ИмяПодсистемы, НомерВерсии, СтандартнаяОбработка);
	КонецЕсли;
	
	Если Не СтандартнаяОбработка Тогда
		Возврат;
	КонецЕсли;
	
	УстановитьПривилегированныйРежим(Истина);
	
	Запрос = Новый Запрос;
	Запрос.Текст =
	"ВЫБРАТЬ
	| ВерсииПодсистем.ИмяПодсистемы КАК ИмяПодсистемы
	|ИЗ
	| РегистрСведений.ВерсииПодсистем КАК ВерсииПодсистем";
	
	ПодсистемыКонфигурации = Запрос.Выполнить().Выгрузить().ВыгрузитьКолонку("ИмяПодсистемы");
	
	Если ПодсистемыКонфигурации.Количество() > 0 Тогда
		// Это не первый запуск программы
		Если ПодсистемыКонфигурации.Найти(ИмяПодсистемы) = Неопределено Тогда
			Запись = РегистрыСведений.ВерсииПодсистем.СоздатьМенеджерЗаписи();
			Запись.ИмяПодсистемы = ИмяПодсистемы;
			Запись.Версия = ?(НомерВерсии = "", "0.0.0.1", НомерВерсии);
			Запись.Записать();
		КонецЕсли;
	КонецЕсли;
	
	Сведения = ОбновлениеИнформационнойБазыСлужебный.СведенияОбОбновленииИнформационнойБазы();
	ИндексЭлемента = Сведения.НовыеПодсистемы.Найти(ИмяПодсистемы);
	Если ИндексЭлемента <> Неопределено Тогда
		Сведения.НовыеПодсистемы.Удалить(ИндексЭлемента);
		ОбновлениеИнформационнойБазыСлужебный.ЗаписатьСведенияОбОбновленииИнформационнойБазы(Сведения);
	КонецЕсли;
	
КонецПроцедуры

// Возвращает номер очереди отложенного обработчика обновления по его полному
// имени или уникальному идентификатору.
//
// Параметры:
//  ИмяИлиИдентификатор - Строка
//                      - УникальныйИдентификатор - полное имя отложенного обработчика
//                        или его идентификатор. Подробнее см. НоваяТаблицаОбработчиковОбновления,
//                        описание свойств Процедура и Идентификатор.
//
// Возвращаемое значение:
//  Число, Неопределено - номер очереди переданного обработчика, если
//                        обработчик не найден - будет возвращено Неопределено.
//
Функция ОчередьОтложенногоОбработчикаОбновления(ИмяИлиИдентификатор) Экспорт
	
	Результат = ОбновлениеИнформационнойБазыСлужебныйПовтИсп.ОчередьОтложенногоОбработчикаОбновления();
	
	Если ТипЗнч(ИмяИлиИдентификатор) = Тип("УникальныйИдентификатор") Тогда
		ОчередьПоИдентификатору = Результат["ПоИдентификатору"];
		Возврат ОчередьПоИдентификатору[ИмяИлиИдентификатор];
	Иначе
		ОчередьПоИмени = Результат["ПоИмени"];
		Возврат ОчередьПоИмени[ИмяИлиИдентификатор];
	КонецЕсли;
	
КонецФункции

// Максимальное количество записей в выборке данных для обновления.
//
// Возвращаемое значение:
//  Число - константа 1000.
//
Функция МаксимальноеКоличествоЗаписейВВыборке() Экспорт
	
	Возврат 1000;
	
КонецФункции

// Возвращает таблицу с данными, которые нужно обновить.
// Используется в многопоточных обработчиках обновления.
//
// Параметры:
//  Параметры - Структура - тот параметр, который передается в обработчик обновления.
//
// Возвращаемое значение:
//  ТаблицаЗначений - для ссылочного объекта с колонками:
//     * Ссылка - ЛюбаяСсылка - ссылка на обновляемый объект.
//     * Дата   - Дата - колонка присутствует только для документов.
//  
//  ТаблицаЗначений - для регистра состав колонок зависит от состава измерений
//                     обновляемого объекта.
//
Функция ДанныеДляОбновленияВМногопоточномОбработчике(Параметры) Экспорт
	
	НаборДанных = Параметры.ОбновляемыеДанные.НаборДанных;
	
	Если НаборДанных.Количество() > 0 Тогда
		Данные = НаборДанных[0].Данные.Скопировать();
		
		Для Индекс = 1 По НаборДанных.Количество() - 1 Цикл
			Набор = НаборДанных[Индекс];
			
			Для Каждого ДанныеНабора Из Набор.Данные Цикл
				СтрокаДанных = Данные.Добавить();
				ЗаполнитьЗначенияСвойств(СтрокаДанных, ДанныеНабора);
			КонецЦикла;
		КонецЦикла;
		
		Если Данные.Количество() > 0 Тогда
			Возврат Данные;
		КонецЕсли;
	КонецЕсли;
	
	Возврат Новый ТаблицаЗначений;
	
КонецФункции

// Возвращает текущее значение приоритета отложенной обработки данных.
//
// Возвращаемое значение:
//  Строка - возможные значения - "ОбработкаДанных" и "РаботаПользователей".
//
Функция ПриоритетОтложеннойОбработки() Экспорт
	СведенияОбОбновлении = ОбновлениеИнформационнойБазыСлужебный.СведенияОбОбновленииИнформационнойБазы();
	Если СведенияОбОбновлении.УправлениеОтложеннымОбновлением.Свойство("ФорсироватьОбновление") Тогда
		Возврат "ОбработкаДанных";
	Иначе
		Возврат "РаботаПользователей";
	КонецЕсли;
КонецФункции

// Позволяет изменить приоритет отложенной обработки данных.
//
// Параметры:
//  Приоритет - Строка - значение приоритета. Допустимые значения - "ОбработкаДанных" или "РаботаПользователей".
//
Процедура УстановитьПриоритетОтложеннойОбработки(Приоритет) Экспорт
	
	НачатьТранзакцию();
	Попытка
		Блокировка = Новый БлокировкаДанных;
		Блокировка.Добавить("Константа.СведенияОбОбновленииИБ");
		Блокировка.Заблокировать();
		
		СведенияОбОбновлении = ОбновлениеИнформационнойБазыСлужебный.СведенияОбОбновленииИнформационнойБазы();
		Если Приоритет = "ОбработкаДанных" Тогда
			СведенияОбОбновлении.УправлениеОтложеннымОбновлением.Вставить("ФорсироватьОбновление");
		Иначе
			СведенияОбОбновлении.УправлениеОтложеннымОбновлением.Удалить("ФорсироватьОбновление");
		КонецЕсли;
		
		ОбновлениеИнформационнойБазыСлужебный.ЗаписатьСведенияОбОбновленииИнформационнойБазы(СведенияОбОбновлении);
		
		ЗафиксироватьТранзакцию();
	Исключение
		ОтменитьТранзакцию();
		ВызватьИсключение;
	КонецПопытки;
	
КонецПроцедуры

// Возвращает количество потоков обновления информационной базы.
//
// Если количество указано в параметре запуска "ЧислоПотоковОбновления", то возвращается оно.
// Иначе, если значение константы КоличествоПотоковОбновленияИнформационнойБазы установлено, то возвращается оно.
// Иначе возвращается значение по умолчанию, см. КоличествоПотоковОбновленияИнформационнойБазыПоУмолчанию.
//
// Возвращаемое значение:
//  Число - количество потоков.
//
Функция КоличествоПотоковОбновления() Экспорт
	Возврат ОбновлениеИнформационнойБазыСлужебный.КоличествоПотоковОбновленияИнформационнойБазы();
КонецФункции

// Позволяет установить количество потоков отложенной обработки данных.
//
// Параметры:
//  Количество - Число - число потоков.
//
Процедура УстановитьКоличествоПотоковОбновления(Количество) Экспорт
	Константы.КоличествоПотоковОбновленияИнформационнойБазы.Установить(Количество);
КонецПроцедуры

// Возвращает признак разрешения использования многопоточного обновления.
// Многопоточное обновление можно включить в ОбновлениеИнформационнойБазыПереопределяемый.ПриОпределенииНастроек().
//
// Возвращаемое значение:
//  Булево - если Истина, многопоточное обновление разрешено. По умолчанию - Ложь (для обратной совместимости).
//
Функция РазрешеноМногопоточноеОбновление() Экспорт
	Возврат ОбновлениеИнформационнойБазыСлужебный.РазрешеноМногопоточноеОбновление();
КонецФункции

// Возвращает прогресс обновления областей данных.
//
// Параметры:
//  РежимОбновления - Строка - определяет, по какому этапу обновления необходимо получить данные.
//                             Доступные значения: "Оперативное", "Отложенное".
//
// Возвращаемое значение:
//  Структура:
//     * Обновлено   - Число - количество областей, этап обновления которых завершен.
//     * Выполняется - Число - количество областей, по которым обновление выполняется.
//     * Ожидают     - Число - количество областей, ожидающих начало этапа обновления.
//     * Проблемы    - Число - количество областей, в процессе обновления которых произошли ошибки.
//     * ОбластиСПроблемами - Массив из Число - номера областей, в процессе обновления которых произошли ошибки.
//     * ОбластиВыполняется - Массив из Число - номера областей, по которым обновление выполняется.
//     * ОбластиОжидает     - Массив из Число - номера областей, ожидающих начало этапа обновления.
//     * ОбластиОбновлено   - Массив из Число - номера областей, этап обновления которых завершен.
//
Функция ПрогрессОбновленияОбластейДанных(РежимОбновления) Экспорт
	
	Если Не ОбщегоНазначения.РазделениеВключено() 
		Или Не ОбщегоНазначения.ПодсистемаСуществует("ТехнологияСервиса.БазоваяФункциональность") 
		Или Не ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.РаботаВМоделиСервиса.ОбновлениеВерсииИБВМоделиСервиса") Тогда
		Возврат Неопределено;
	КонецЕсли;
	
	РежимОбновленияОперативное = "Оперативное";
	РежимОбновленияОтложенное  = "Отложенное";
	
	Если РежимОбновления = РежимОбновленияОперативное Тогда
		РежимыВыполнения = Новый Массив;
		РежимыВыполнения.Добавить(Перечисления.РежимыВыполненияОбработчиков.Оперативно);
		РежимыВыполнения.Добавить(Перечисления.РежимыВыполненияОбработчиков.Монопольно);
	ИначеЕсли РежимОбновления = РежимОбновленияОтложенное Тогда
		РежимыВыполнения = ОбщегоНазначенияКлиентСервер.ЗначениеВМассиве(
			Перечисления.РежимыВыполненияОбработчиков.Отложенно);
	Иначе
		ВызватьИсключение СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Некорректное значение параметра ""%1"".
				 |Доступные значения: ""%2"", ""%3""'"), "РежимОбновления", РежимОбновленияОперативное, РежимОбновленияОтложенное);
	КонецЕсли;
	
	ПорядокСостоянияОбновлено = 4;
	ПорядокСостоянияОжидает = 3;
	ПорядокСостоянияВыполняется = 2;
	ПорядокСостоянияОшибка = 1;
	
	Запрос = Новый Запрос;
	
	Запрос.УстановитьПараметр("РежимыВыполнения", РежимыВыполнения);
	Запрос.УстановитьПараметр("РежимОбновления", РежимОбновления);
	Запрос.УстановитьПараметр("РежимОбновленияОперативное", РежимОбновленияОперативное);
	Запрос.УстановитьПараметр("РежимОбновленияОтложенное", РежимОбновленияОтложенное);
	
	Запрос.УстановитьПараметр("ПорядокСостоянияОбновлено", ПорядокСостоянияОбновлено);
	Запрос.УстановитьПараметр("ПорядокСостоянияВыполняется", ПорядокСостоянияВыполняется);
	Запрос.УстановитьПараметр("ПорядокСостоянияОжидает", ПорядокСостоянияОжидает);
	Запрос.УстановитьПараметр("ПорядокСостоянияОшибка", ПорядокСостоянияОшибка);
	
	МодульОбновлениеИнформационнойБазыСлужебныйВМоделиСервиса = ОбщегоНазначения.ОбщийМодуль("ОбновлениеИнформационнойБазыСлужебныйВМоделиСервиса");
	ОбластиОбновленныеНаВерсию = МодульОбновлениеИнформационнойБазыСлужебныйВМоделиСервиса.ОбластиОбновленныеНаВерсию(Метаданные.Имя, Метаданные.Версия);
	
	МодульРаботаВМоделиСервиса = ОбщегоНазначения.ОбщийМодуль("РаботаВМоделиСервиса");
	Запрос.УстановитьПараметр("ИспользуемыеОбласти", МодульРаботаВМоделиСервиса.ИспользуемыеОбластиДанных().Выгрузить());
	Запрос.УстановитьПараметр("ОбновленныеОбласти", ОбластиОбновленныеНаВерсию);
	
	Запрос.Текст =
	"ВЫБРАТЬ
	|	ИспользуемыеОбласти.ОбластьДанных КАК ОбластьДанных
	|ПОМЕСТИТЬ ИспользуемыеОбласти
	|ИЗ
	|	&ИспользуемыеОбласти КАК ИспользуемыеОбласти
	|;
	|
	|////////////////////////////////////////////////////////////////////////////////
	|ВЫБРАТЬ
	|	ОбновленныеОбласти.ОбластьДанныхВспомогательныеДанные КАК ОбластьДанныхВспомогательныеДанные,
	|	ОбновленныеОбласти.ВыполненаРегистрацияОтложенныхОбработчиков КАК ВыполненаРегистрацияОтложенныхОбработчиков
	|ПОМЕСТИТЬ ОбновленныеОбласти
	|ИЗ
	|	&ОбновленныеОбласти КАК ОбновленныеОбласти
	|;
	|
	|////////////////////////////////////////////////////////////////////////////////
	|ВЫБРАТЬ
	|	ИспользуемыеОбласти.ОбластьДанных КАК ОбластьДанных,
	|	НЕ ОбновленныеОбласти.ОбластьДанныхВспомогательныеДанные ЕСТЬ NULL КАК ОперативноеОбновлениеЗавершено,
	|	НЕ ОбновленныеОбласти.ОбластьДанныхВспомогательныеДанные ЕСТЬ NULL
	|		И НЕ ОбновленныеОбласти.ВыполненаРегистрацияОтложенныхОбработчиков КАК ВыполняетсяРегистрацияОтложенныхОбработчиков
	|ПОМЕСТИТЬ ОбластиДанных
	|ИЗ
	|	ИспользуемыеОбласти КАК ИспользуемыеОбласти
	|		ЛЕВОЕ СОЕДИНЕНИЕ ОбновленныеОбласти КАК ОбновленныеОбласти
	|		ПО (ОбновленныеОбласти.ОбластьДанныхВспомогательныеДанные = ИспользуемыеОбласти.ОбластьДанных)
	|;
	|
	|////////////////////////////////////////////////////////////////////////////////
	|ВЫБРАТЬ
	|	ОбластиДанных.ОбластьДанных КАК ОбластьДанных,
	|	МИНИМУМ(ВЫБОР
	|			КОГДА &РежимОбновления = &РежимОбновленияОперативное
	|					И ОбластиДанных.ОперативноеОбновлениеЗавершено
	|				ТОГДА ВЫБОР
	|						КОГДА ОбластиДанных.ВыполняетсяРегистрацияОтложенныхОбработчиков
	|							ТОГДА &ПорядокСостоянияВыполняется
	|						ИНАЧЕ &ПорядокСостоянияОбновлено
	|					КОНЕЦ
	|			КОГДА &РежимОбновления = &РежимОбновленияОтложенное
	|					И НЕ ОбластиДанных.ОперативноеОбновлениеЗавершено
	|				ТОГДА &ПорядокСостоянияОжидает
	|			КОГДА &РежимОбновления = &РежимОбновленияОтложенное
	|					И ЕСТЬNULL(ОбработчикиОбновления.Статус, ЗНАЧЕНИЕ(Перечисление.СтатусыОбработчиковОбновления.Выполнен)) = ЗНАЧЕНИЕ(Перечисление.СтатусыОбработчиковОбновления.Выполнен)
	|				ТОГДА &ПорядокСостоянияОбновлено
	|			КОГДА ОбработчикиОбновления.Статус = ЗНАЧЕНИЕ(Перечисление.СтатусыОбработчиковОбновления.Выполняется)
	|				ТОГДА &ПорядокСостоянияВыполняется
	|			КОГДА ОбработчикиОбновления.Статус = ЗНАЧЕНИЕ(Перечисление.СтатусыОбработчиковОбновления.Ошибка)
	|				ТОГДА &ПорядокСостоянияОшибка
	|			ИНАЧЕ &ПорядокСостоянияОжидает
	|		КОНЕЦ) КАК ПорядокСостояния
	|ПОМЕСТИТЬ СтатистикаОбновления
	|ИЗ
	|	ОбластиДанных КАК ОбластиДанных
	|		ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ОбработчикиОбновления КАК ОбработчикиОбновления
	|		ПО (ОбработчикиОбновления.ОбластьДанныхВспомогательныеДанные = ОбластиДанных.ОбластьДанных)
	|			И (ОбработчикиОбновления.РежимВыполнения В (&РежимыВыполнения))
	|
	|СГРУППИРОВАТЬ ПО
	|	ОбластиДанных.ОбластьДанных
	|;
	|
	|////////////////////////////////////////////////////////////////////////////////
	|ВЫБРАТЬ
	|	СтатистикаОбновления.ПорядокСостояния КАК ПорядокСостояния,
	|	СтатистикаОбновления.ОбластьДанных КАК ОбластьДанных
	|ИЗ
	|	СтатистикаОбновления КАК СтатистикаОбновления
	|
	|УПОРЯДОЧИТЬ ПО
	|	ОбластьДанных
	|ИТОГИ
	|	КОЛИЧЕСТВО(РАЗЛИЧНЫЕ ОбластьДанных)
	|ПО
	|	ПорядокСостояния";
	
	ПрогрессОбновления = ОбновлениеИнформационнойБазыСлужебный.НовыйПрогрессОбновленияОбластейДанных();
	
	Результат = Запрос.Выполнить();
	Если Результат.Пустой() Тогда
		Возврат ПрогрессОбновления;
	КонецЕсли;
	
	ВыборкаПорядокСостояния = Результат.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам);
	Пока ВыборкаПорядокСостояния.Следующий() Цикл
		
		Если ВыборкаПорядокСостояния.ПорядокСостояния = ПорядокСостоянияОбновлено Тогда
			ПрогрессОбновления.Обновлено = ВыборкаПорядокСостояния.ОбластьДанных;
			Расшифровка = ПрогрессОбновления.ОбластиОбновлено;
		ИначеЕсли ВыборкаПорядокСостояния.ПорядокСостояния = ПорядокСостоянияВыполняется Тогда
			ПрогрессОбновления.Выполняется = ВыборкаПорядокСостояния.ОбластьДанных;
			Расшифровка = ПрогрессОбновления.ОбластиВыполняется;
		ИначеЕсли ВыборкаПорядокСостояния.ПорядокСостояния = ПорядокСостоянияОшибка Тогда
			ПрогрессОбновления.Проблемы = ВыборкаПорядокСостояния.ОбластьДанных;
			Расшифровка = ПрогрессОбновления.ОбластиСПроблемами;
		Иначе
			ПрогрессОбновления.Ожидают = ВыборкаПорядокСостояния.ОбластьДанных;
			Расшифровка = ПрогрессОбновления.ОбластиОжидает;
		КонецЕсли;
		
		ВыборкаОбласти = ВыборкаПорядокСостояния.Выбрать();
		Пока ВыборкаОбласти.Следующий() Цикл
			Расшифровка.Добавить(ВыборкаОбласти.ОбластьДанных);
		КонецЦикла;
		
	КонецЦикла;
	
	Возврат ПрогрессОбновления;
	
КонецФункции

// Возвращает таблицу обработчиков обновления по указанному отбору.
// В режиме сервиса, если не задан отбор по областям данных,
// возвращаются все обработчики - разделенные и неразделенные.
// 
// Параметры:
//  Отбор - Структура:
//     * РежимыВыполнения - Массив из Строка - доступные значения соответствуют именам значений
//                                             перечисления РежимыВыполненияОбработчиков.
//     * Статусы - Массив из Строка - доступные значения соответствуют именам значений
//                                    перечисления СтатусыОбработчиковОбновления.
//     * ОбластиДанных - Массив из Число - номера областей, по которым нужно получить обработчики.
//
// Возвращаемое значение:
//  ТаблицаЗначений:
//     * ИмяОбработчика - Строка
//     * РежимВыполнения - Строка - имя соответствующего значения перечисления РежимыВыполненияОбработчиков
//     * ИмяБиблиотеки - Строка
//     * Версия - Строка
//     * Статус - Строка - имя соответствующего значения перечисления СтатусыОбработчиковОбновления
//     * ДлительностьОбработки - Число
//     * ИнформацияОбОшибке - Строка
//     * ОбластьДанных - Число
//
Функция ОбработчикиОбновления(Отбор = Неопределено) Экспорт
	
	Если Отбор = Неопределено Тогда
		Отбор = Новый Структура;
	КонецЕсли;
	
	Запрос = Новый Запрос;
	Запрос.Текст =
	"ВЫБРАТЬ
	|	ОбработчикиОбновления.ИмяОбработчика КАК ИмяОбработчика,
	|	ОбработчикиОбновления.РежимВыполнения КАК РежимВыполненияСсылка,
	|	ОбработчикиОбновления.ИмяБиблиотеки КАК ИмяБиблиотеки,
	|	ОбработчикиОбновления.Версия КАК Версия,
	|	ОбработчикиОбновления.Статус КАК СтатусСсылка,
	|	ОбработчикиОбновления.ДлительностьОбработки КАК ДлительностьОбработки,
	|	ОбработчикиОбновления.ИнформацияОбОшибке КАК ИнформацияОбОшибке,
	|	ОбработчикиОбновления.ОбластьДанныхВспомогательныеДанные КАК ОбластьДанных
	|ИЗ
	|	РегистрСведений.ОбработчикиОбновления КАК ОбработчикиОбновления
	|ГДЕ
	|	&УсловиеОбработчиковОбластей
	|
	|ОБЪЕДИНИТЬ ВСЕ
	|
	|ВЫБРАТЬ
	|	ОбработчикиОбновления.ИмяОбработчика,
	|	ОбработчикиОбновления.РежимВыполнения,
	|	ОбработчикиОбновления.ИмяБиблиотеки,
	|	ОбработчикиОбновления.Версия,
	|	ОбработчикиОбновления.Статус,
	|	ОбработчикиОбновления.ДлительностьОбработки,
	|	ОбработчикиОбновления.ИнформацияОбОшибке,
	|	0
	|ИЗ
	|	РегистрСведений.ОбработчикиОбновленияОбщихДанных КАК ОбработчикиОбновления
	|ГДЕ
	|	&УсловиеОбработчиковОбщихДанных";
	
	УсловияЗапроса = Новый Массив;
	
	ЕстьФильтрОперативноеОбновление = Ложь;
	Если Отбор.Свойство("РежимыВыполнения") Тогда
		РежимыВыполнения = Новый Массив;
		Для Каждого ИмяЗначения Из Отбор.РежимыВыполнения Цикл
			РежимВыполнения = ОбновлениеИнформационнойБазыСлужебный.ЗначениеПеречисленияПоИмени(ИмяЗначения,
				Метаданные.Перечисления.РежимыВыполненияОбработчиков);
			РежимыВыполнения.Добавить(РежимВыполнения);
			Если РежимВыполнения = Перечисления.РежимыВыполненияОбработчиков.Оперативно
				Или РежимВыполнения = Перечисления.РежимыВыполненияОбработчиков.Монопольно Тогда
				ЕстьФильтрОперативноеОбновление = Истина;
			КонецЕсли;
		КонецЦикла;
		УсловияЗапроса.Добавить("ОбработчикиОбновления.РежимВыполнения В (&РежимыВыполнения)");
		Запрос.УстановитьПараметр("РежимыВыполнения", РежимыВыполнения);
	КонецЕсли;
	
	ЕстьФильтрВыполняется = Ложь;
	Если Отбор.Свойство("Статусы") Тогда
		Статусы = Новый Массив;
		Для Каждого ИмяЗначения Из Отбор.Статусы Цикл
			Статус = ОбновлениеИнформационнойБазыСлужебный.ЗначениеПеречисленияПоИмени(ИмяЗначения,
				Метаданные.Перечисления.СтатусыОбработчиковОбновления);
			Статусы.Добавить(Статус);
			Если Статус = Перечисления.СтатусыОбработчиковОбновления.Выполняется Тогда
				ЕстьФильтрВыполняется = Истина;
			КонецЕсли;
		КонецЦикла;
		УсловияЗапроса.Добавить("ОбработчикиОбновления.Статус В (&Статусы)");
		Запрос.УстановитьПараметр("Статусы", Статусы);
	КонецЕсли;
	
	Если ЗначениеЗаполнено(УсловияЗапроса) Тогда
		УсловиеОбработчиковОбщихДанных = СтрСоединить(УсловияЗапроса, Символы.ПС + " И ");
	Иначе
		УсловиеОбработчиковОбщихДанных = "ИСТИНА";
	КонецЕсли;
	
	Если Отбор.Свойство("ОбластиДанных") Тогда
		ОбластиДанных = Отбор.ОбластиДанных;
	ИначеЕсли ОбщегоНазначения.РазделениеВключено() Тогда
		МодульРаботаВМоделиСервиса = ОбщегоНазначения.ОбщийМодуль("РаботаВМоделиСервиса");
		ОбластиДанных = МодульРаботаВМоделиСервиса.ИспользуемыеОбластиДанных().Выгрузить().ВыгрузитьКолонку(
			"ОбластьДанных");
	Иначе
		ОбластиДанных = Неопределено;
	КонецЕсли;
	
	Если ОбластиДанных <> Неопределено Тогда
		УсловияЗапроса.Добавить("ОбработчикиОбновления.ОбластьДанныхВспомогательныеДанные В (&ОбластиДанных)");
		Запрос.УстановитьПараметр("ОбластиДанных", ОбластиДанных);
		Если Отбор.Свойство("ОбластиДанных") И Отбор.ОбластиДанных.Найти(0) = Неопределено Тогда
			УсловиеОбработчиковОбщихДанных = "ЛОЖЬ";
		КонецЕсли;
	КонецЕсли;
	
	Если ЗначениеЗаполнено(УсловияЗапроса) Тогда
		УсловиеОбработчиковОбластей = СтрСоединить(УсловияЗапроса, Символы.ПС + " И ");
	Иначе
		УсловиеОбработчиковОбластей = "ИСТИНА";
	КонецЕсли;
	
	Запрос.Текст = СтрЗаменить(Запрос.Текст, "&УсловиеОбработчиковОбластей", УсловиеОбработчиковОбластей);
	Запрос.Текст = СтрЗаменить(Запрос.Текст, "&УсловиеОбработчиковОбщихДанных", УсловиеОбработчиковОбщихДанных);
	
	ИнформацияПоОбработчикам = ОбновлениеИнформационнойБазыСлужебный.НоваяТаблицаИнформацииОбОбработчиках();
	ИменаРежимовПоЗначению = ОбновлениеИнформационнойБазыСлужебный.ИменаПоЗначениямПеречисления(Метаданные.Перечисления.РежимыВыполненияОбработчиков);
	ИменаСтатусовПоЗначению = ОбновлениеИнформационнойБазыСлужебный.ИменаПоЗначениямПеречисления(Метаданные.Перечисления.СтатусыОбработчиковОбновления);
	
	Выборка = Запрос.Выполнить().Выбрать();
	Пока Выборка.Следующий() Цикл
		
		Строка = ИнформацияПоОбработчикам.Добавить();
		ЗаполнитьЗначенияСвойств(Строка, Выборка);
		Строка.РежимВыполнения = ИменаРежимовПоЗначению[Выборка.РежимВыполненияСсылка];
		Строка.Статус = ИменаСтатусовПоЗначению[Выборка.СтатусСсылка];
		
	КонецЦикла;
	
	Если ЕстьФильтрОперативноеОбновление И ЕстьФильтрВыполняется 
		И ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.РаботаВМоделиСервиса.ОбновлениеВерсииИБВМоделиСервиса") Тогда
		МодульОбновлениеИнформационнойБазыСлужебныйВМоделиСервиса = ОбщегоНазначения.ОбщийМодуль("ОбновлениеИнформационнойБазыСлужебныйВМоделиСервиса");
		ОбновленныеОбласти = МодульОбновлениеИнформационнойБазыСлужебныйВМоделиСервиса.ОбластиОбновленныеНаВерсию(Метаданные.Имя, Метаданные.Версия);
		ОбластиВыполняетсяРегистрация = ОбновленныеОбласти.НайтиСтроки(
			Новый Структура("ВыполненаРегистрацияОтложенныхОбработчиков", Ложь));
		Для Каждого ЭлементВыполняетсяРегистрация Из ОбластиВыполняетсяРегистрация Цикл
			НомерОбласти = ЭлементВыполняетсяРегистрация.ОбластьДанныхВспомогательныеДанные;
			Если ОбластиДанных <> Неопределено И ОбластиДанных.Найти(НомерОбласти) = Неопределено Тогда
				Продолжить;
			КонецЕсли;
			Строка = ИнформацияПоОбработчикам.Добавить();
			Строка.ИмяОбработчика = НСтр("ru = 'Служебные процедуры регистрации отложенных обработчиков'");
			Строка.РежимВыполнения = ИменаРежимовПоЗначению[Перечисления.РежимыВыполненияОбработчиков.Оперативно];
			Строка.ИмяБиблиотеки = "БиблиотекаСтандартныхПодсистем";
			Строка.Статус = ИменаСтатусовПоЗначению[Перечисления.СтатусыОбработчиковОбновления.Выполняется];
			Строка.ОбластьДанных = НомерОбласти;
		КонецЦикла;
	КонецЕсли;
	
	Возврат ИнформацияПоОбработчикам;
	
КонецФункции

// Возвращает таблицу обновляемых объектов конфигурации со списком обработчиков,
// которые их обрабатывают на текущую версию.
//
// Возвращаемое значение:
//  Соответствие из КлючИЗначение:
//     * Ключ - Строка - полное имя объекта.
//     * Значение - Массив из Строка - имена выполняемых обработчиков обновления.
//
Функция ОбновляемыеОбъекты() Экспорт
	
	Запрос = Новый Запрос;
	Запрос.УстановитьПараметр("РежимВыполнения", Перечисления.РежимыВыполненияОбработчиков.Отложенно);
	Запрос.Текст =
		"ВЫБРАТЬ
		|	ОбработчикиОбновления.ИмяОбработчика КАК ИмяОбработчика,
		|	ОбработчикиОбновления.ИзменяемыеОбъекты КАК ИзменяемыеОбъекты
		|ИЗ
		|	РегистрСведений.ОбработчикиОбновления КАК ОбработчикиОбновления
		|ГДЕ
		|	ОбработчикиОбновления.РежимВыполнения = &РежимВыполнения";
	Обработчики = Запрос.Выполнить().Выгрузить();
	ОбновляемыеОбъекты = Новый Соответствие;
	Для Каждого Обработчик Из Обработчики Цикл
		ИзменяемыеОбъектыЧастями = СтрРазделить(Обработчик.ИзменяемыеОбъекты, ",", Ложь);
		Для Каждого ИзменяемыйОбъект Из ИзменяемыеОбъектыЧастями Цикл
			ИзменяемыйОбъект = СокрЛП(ИзменяемыйОбъект);
			Если ОбновляемыеОбъекты[ИзменяемыйОбъект] = Неопределено Тогда
				ОбновляемыеОбъекты[ИзменяемыйОбъект] = Новый Массив;
			КонецЕсли;
			Обработчики = ОбновляемыеОбъекты[ИзменяемыйОбъект]; // Массив
			Обработчики.Добавить(Обработчик.ИмяОбработчика);
		КонецЦикла;
	КонецЦикла;
	
	Возврат ОбновляемыеОбъекты;
	
КонецФункции

// Добавляет очередной устаревший объект в список объектов, которые
// планируются к удалению в будущих версиях программы.
//
// Параметры:
//  Объекты - Соответствие из КлючИЗначение:
//   * Ключ - Строка - смотри ниже параметр Объект.
//   * Значение - Булево, Соответствие - смотри ниже параметр Уточнение.
//
//  Объект - Строка - полное имя объекта метаданных с данными, который следует очистить.
//     Либо объект метаданных с основной таблицей, например, "Справочник.УдалитьОчередьЗаданий".
//     Либо значение перечисления, например, "Перечисление.ХозяйственныеОперации.УдалитьСписаниеТоваровПереданныхПартнерам".
//     Либо точка маршрута, например, "БизнесПроцесс.Задание.ТочкаМаршрута.УдалитьВернутьИсполнителю".
//     Либо все точки маршрута, например, "БизнесПроцесс.Задание.Точки".
//     Либо поле регистра, входящее в уникальный индекс,
//     например, измерение "РегистрСведений._ДемоОтветственныеЛица.Склад".
//
//  Уточнение - Булево - когда в параметре Объект указан удаляемый в будущем объект.
//               Имя объекта должно начинаться с Удалить.
//               По умолчанию Ложь, если указано Истина, то данные объекта будут удалены.
//            - ОписаниеТипов, Тип, ПеречислениеСсылка, ТочкаМаршрутаБизнесПроцессаСсылка - когда
//               в параметре Объект указано поле тип которого сокращается
//               на список указанных ссылочных типов или один тип
//               или значение перечисления или значение точки маршрута бизнес-процесса.
//
Процедура ДобавитьОбъектПланируемыйКУдалению(Объекты, Объект, Уточнение = Ложь) Экспорт
	
	Если ТипЗнч(Уточнение) = Тип("Булево") Тогда
		Объекты.Вставить(Объект, Уточнение);
	Иначе
		СокращаемыеТипыИЗначения = Объекты.Получить(Объект);
		Если СокращаемыеТипыИЗначения = Неопределено Тогда
			СокращаемыеТипыИЗначения = Новый Соответствие;
			Объекты.Вставить(Объект, СокращаемыеТипыИЗначения);
		КонецЕсли;
		Если ТипЗнч(Уточнение) = Тип("ОписаниеТипов") Тогда
			Для Каждого Тип Из Уточнение.Типы() Цикл
				СокращаемыеТипыИЗначения.Вставить(Тип, Истина);
			КонецЦикла;
		Иначе
			СокращаемыеТипыИЗначения.Вставить(Уточнение, Истина);
		КонецЕсли;
	КонецЕсли;
	
КонецПроцедуры

#Область ДляВызоваИзДругихПодсистем

// ИнтернетПоддержкаПользователей.ПолучениеОбновленийПрограммы

// Возвращает статус отложенных обработчиков обновления.
//
// Возвращаемое значение:
//  Строка - пустая строка, если все обработчики успешно выполнились.
//           "СтатусНеВыполнено" - есть невыполненные обработчики.
//           "СтатусОшибка" - все обработчики завершились, но есть хотя бы один с ошибкой.
//           "СтатусПриостановлен" - все обработчики завершились, но есть хотя бы один остановленный.
//
Функция СтатусОтложенногоОбновления() Экспорт
	
	Возврат ОбновлениеИнформационнойБазыСлужебный.СтатусНевыполненныхОбработчиков();
	
КонецФункции

// Конец ИнтернетПоддержкаПользователей.ПолучениеОбновленийПрограммы

#КонецОбласти

////////////////////////////////////////////////////////////////////////////////
// Начальное заполнение элементов.

#Область ЗаполнениеПредопределенныхЭлементов

////////////////////////////////////////////////////////////////////////////////
// Прочие процедуры и функции.

// Зарегистрировать предопределенные элементы для обновления в обработчике обновления.
//
// Параметры:
//  Параметры        - Структура - служебные параметры обработчика обновления.
//  ОбъектМетаданных - ОбъектМетаданных
//                   - Неопределено - обновляемые объекты
//  ДополнительныеПараметры - Структура:
//   *  РежимОбновления  - Строка - определяет вариант регистрация предопределенных элементов для обновления. Варианты:
//                              Все - будут зарегистрированы все предопределенные элементы;
//                              НовыеИИзмененные - обновляет только новые и измененные;
//                              МультиязычныеСтроки - только, если были изменения в мультиязычных реквизитах.
//   * ПропускатьПустые  - Булево -  если Истина, то пустые строки в поставляемых данных исключаются из проверки на изменение.
//                            Например, объект не будет зарегистрирован, когда в ИБ реквизит заполнен, а в коде пустая строка.
//   * СравниватьТабличныеЧасти - Булево - если Ложь, то табличные части игнорируются и не будут сравниваться на различие.
//
Процедура ЗарегистрироватьПредопределенныеЭлементыДляОбновления(Параметры, ОбъектМетаданных = Неопределено, ДополнительныеПараметры = Неопределено) Экспорт
	
	ОбновлениеИнформационнойБазыСлужебный.ЗарегистрироватьПредопределенныеЭлементыДляОбновления(Параметры, ОбъектМетаданных, ДополнительныеПараметры);
	
КонецПроцедуры

// Заполняет предопределенные элементы объекта в обработчике обновления поставляемыми данными.
//
// Параметры:
//  Параметры           - Структура- служебные параметры обработчика обновления.
//  ОбъектМетаданных    - ОбъектМетаданных - заполняемый объект.
//  НастройкиЗаполнения - см. НастройкиЗаполнения
//
Процедура ЗаполнитьЭлементыНачальнымиДанными(Параметры, ОбъектМетаданных, НастройкиЗаполнения = Неопределено) Экспорт
	
	// Обратная совместимость
	Если ТипЗнч(НастройкиЗаполнения) = Тип("Булево") Тогда
		ОбновитьМультиязычныеСтроки = НастройкиЗаполнения;
		НастройкиЗаполнения = НастройкиЗаполнения();
		НастройкиЗаполнения.ОбновитьТолькоМультиязычныеСтроки = ОбновитьМультиязычныеСтроки;
	ИначеЕсли НастройкиЗаполнения = Неопределено Тогда
		НастройкиЗаполнения = НастройкиЗаполнения();
	КонецЕсли;
	
	ОбновлениеИнформационнойБазыСлужебный.ЗаполнитьЭлементыНачальнымиДанными(Параметры, ОбъектМетаданных, НастройкиЗаполнения);
	
КонецПроцедуры

// Заполнить объект предопределенными данными из кода начального заполнения
// 
// Параметры:
//  ЗаполняемыйОбъект - СправочникОбъект
//                    - ПланВидовХарактеристикОбъект - заполняемый объект
//  НастройкиЗаполнения - см. НастройкиЗаполнения
// 
Процедура ЗаполнитьОбъектНачальнымиДанными(ЗаполняемыйОбъект, НастройкиЗаполнения) Экспорт
	
	ОбновлениеИнформационнойБазыСлужебный.ЗаполнитьОбъектНачальнымиДанными(ЗаполняемыйОбъект, НастройкиЗаполнения);
	
КонецПроцедуры


// Настройки заполнения предопределенных и поставляемых элементов.
// 
// Возвращаемое значение:
//  Структура:
//   * ОбновитьТолькоМультиязычныеСтроки - Булево - если Истина, то будет обновлены только мультиязычные строки.
//   * Реквизиты - Строка - список реквизитов через запятую, которые будут обновлены. Например, "Наименование,Комментарий".
//
Функция НастройкиЗаполнения() Экспорт
	
	Результат = Новый Структура;
	Результат.Вставить("ОбновитьТолькоМультиязычныеСтроки", Ложь);
	Результат.Вставить("Реквизиты", "");

	Возврат Результат;
	
КонецФункции

#КонецОбласти

#Область УстаревшиеПроцедурыИФункции

// Устарела: больше не требуется, т.к. данные действия выполняются автоматически механизмом обновления.
// 
// Удаляет отложенный обработчик из очереди выполняемых обработчиков на новую версию.
// Следует использовать, например, при переводе отложенного обработчика
// на монопольный (оперативный) режим выполнения.
// Для этого необходимо добавить новый разделенный обработчик обновления с режимом выполнения
// "Оперативно" и признаком "ОбщиеДанные = Ложь", после чего разместить в нем вызов данного метода.
//
// Параметры:
//  ИмяОбработчика - Строка - полное имя процедуры отложенного обработчика.
//
Процедура УдалитьОтложенныйОбработчикИзОчереди(ИмяОбработчика) Экспорт
	Возврат;
КонецПроцедуры

#КонецОбласти


// Записывает в журнал регистрации событие при выполнении обработчика обновления.
// При записи ошибки или предупреждения сохраняет эту информацию в сведениях об обработчике
// обновления для дальнейшей индикации в интерфейсах механизма обновления.
//
// Параметры:
//  Комментарий - Строка - текст сообщения, записываемый в журнал регистрации.
//  Уровень     - УровеньЖурналаРегистрации - уровень важности события.
//                Если не указано, то записывается событие с уровнем Ошибка.
//  Параметры   - Структура - параметры, переданные на вход обработчика обновления.
//
Процедура ЗаписатьСобытиеВЖурналРегистрации(Комментарий, Уровень = Неопределено, Параметры = Неопределено) Экспорт
	
	Если ТипЗнч(Параметры) = Тип("Структура")
		И Параметры.Свойство("ИмяОбработчика") Тогда
		ИмяОбработчика = Параметры.ИмяОбработчика;
	Иначе
		ИмяОбработчика = ПараметрыСеанса.ПараметрыОбработчикаОбновления.ИмяОбработчика;
	КонецЕсли;
	
	Если Уровень = УровеньЖурналаРегистрации.Ошибка
		Или Уровень = Неопределено Тогда
		ОбновлениеИнформационнойБазыСлужебный.ДобавитьИнформациюОбОшибкеВОбработчике(ИмяОбработчика);
		ОбновлениеИнформационнойБазыСлужебный.ЗаписатьОшибку(Комментарий);
	ИначеЕсли Уровень = УровеньЖурналаРегистрации.Предупреждение Тогда
		ОбновлениеИнформационнойБазыСлужебный.ДобавитьИнформациюОбОшибкеВОбработчике(ИмяОбработчика);
		ОбновлениеИнформационнойБазыСлужебный.ЗаписатьПредупреждение(Комментарий);
	Иначе
		ОбновлениеИнформационнойБазыСлужебный.ЗаписатьИнформацию(Комментарий);
	КонецЕсли;
	
КонецПроцедуры

// Выполняет перезапуск отложенного обновления в базе, где уже выполнились
// монопольные и оперативные обработчики обновления.
// Может быть применимо для случаев, когда вносились исправления в процедурах
// регистрации или самих обработчиках обновления и нужно запустить отложенное обновление
// с начала.
//
// Выполняет следующие действия:
//  - останавливает выполнение регламентного задания по отложенному обновлению.
//  - делает повторную регистрацию обновляемых данных:
//    - (по умолчанию)для обработчиков которые еще не выполнились на момент перезапуска.
//    - для переданных обработчиков обновления из списка зарегистрированных к обновлению
//      на текущую версию. Для этого требуется передать в отборе параметр Обработчики.
//    - обработчиков подсистем с указанной версии, в т.ч. тех, которые уже выполнены.
//      Для этого необходимо передать в отборе параметр Подсистемы, в котором указать имя подсистемы
//      и номер версии, с которого требуется отобрать обработчики обновления.
//  - включает регламентное задание отложенного обновления.
//
// Параметры:
//  Отбор - Структура:
//             * Ключ     - Строка - имя подсистемы.
//             * Значение - Строка - номер версии
//        - Массив - имена обработчиков обновления, которые требуется перезапустить.
//
Процедура ПерезапуститьОтложенноеОбновление(Отбор = Неопределено) Экспорт
	
	Если ОбщегоНазначения.ЭтоПодчиненныйУзелРИБ() Тогда
		Возврат;
	КонецЕсли;
	
	Если ТипЗнч(Отбор) = Тип("Структура")
		И Отбор.Количество() = 0 Тогда
		ВызватьИсключение НСтр("ru = 'В отборе не указан список подсистем, для которых требуется перезапустить отложенные обработчики.'");
	КонецЕсли;
	
	// Отключение отложенного обновления.
	ОбновлениеИнформационнойБазыСлужебный.ПриВключенииОтложенногоОбновления(Ложь);
	
	Если Не ОбщегоНазначения.РазделениеВключено() Тогда
		ОтборЗаданий = Новый Структура;
		ОтборЗаданий.Вставить("ИмяМетода", "ОбновлениеИнформационнойБазыСлужебный.ВыполнитьОтложенноеОбновление");
		ОтборЗаданий.Вставить("Состояние", СостояниеФоновогоЗадания.Активно);
		НайденныеЗадания = ФоновыеЗадания.ПолучитьФоновыеЗадания(ОтборЗаданий);
		Для Каждого ЗаданиеОбновления Из НайденныеЗадания Цикл
			Если ЗаданиеОбновления.Состояние = СостояниеФоновогоЗадания.Активно Тогда
				ЗаданиеОбновления.Отменить();
			КонецЕсли;
		КонецЦикла;
	КонецЕсли;
	
	Группы = ОбновлениеИнформационнойБазыСлужебный.НовоеОписаниеГруппПотоковОбработчиковОтложенногоОбновления();
	ОбновлениеИнформационнойБазыСлужебный.ОтменитьВыполнениеВсехПотоков(Группы);
	
	// Повторная регистрация данных.
	ЗапросВременных = Новый Запрос;
	ЗапросВременных.Текст =
		"ВЫБРАТЬ
		|	ОбновлениеИнформационнойБазы.Ссылка КАК Ссылка
		|ИЗ
		|	ПланОбмена.ОбновлениеИнформационнойБазы КАК ОбновлениеИнформационнойБазы
		|ГДЕ
		|	ОбновлениеИнформационнойБазы.Временная = ИСТИНА";
	
	Блокировка = Новый БлокировкаДанных;
	ЭлементБлокировки = Блокировка.Добавить("ПланОбмена.ОбновлениеИнформационнойБазы");
	ЭлементБлокировки.Режим = РежимБлокировкиДанных.Разделяемый;
	
	НачатьТранзакцию();
	Попытка
		Блокировка.Заблокировать();
		
		ВременныеОчереди = ЗапросВременных.Выполнить().Выгрузить();
		Для Каждого ВременнаяОчередь Из ВременныеОчереди Цикл
			ПланыОбмена.УдалитьРегистрациюИзменений(ВременнаяОчередь.Ссылка);
		КонецЦикла;
		ЗафиксироватьТранзакцию();
	Исключение
		ОтменитьТранзакцию();
		ВызватьИсключение;
	КонецПопытки;
	
	СведенияОБлокируемыхОбъектах = ОбновлениеИнформационнойБазыСлужебный.СведенияОБлокируемыхОбъектах();
	ПараметрыРегистрации = Новый Структура;
	ПараметрыРегистрации.Вставить("ПриЗапускеКлиентскогоПриложения", Ложь);
	ПараметрыРегистрации.Вставить("ПерезапускОбновления", Истина);
	
	// Список обработчиков, которые не завершены на данный момент.
	Запрос = Новый Запрос;
	Запрос.УстановитьПараметр("РежимВыполненияОбработчика", Перечисления.РежимыВыполненияОтложенныхОбработчиков.Параллельно);
	Запрос.УстановитьПараметр("Статус", Перечисления.СтатусыОбработчиковОбновления.Выполнен);
	Запрос.Текст =
		"ВЫБРАТЬ
		|	ОбработчикиОбновления.ИмяОбработчика КАК ИмяОбработчика
		|ИЗ
		|	РегистрСведений.ОбработчикиОбновления КАК ОбработчикиОбновления
		|ГДЕ
		|	ОбработчикиОбновления.РежимВыполненияОтложенногоОбработчика = &РежимВыполненияОбработчика
		|	И ОбработчикиОбновления.Статус <> &Статус";
	
	Если ТипЗнч(Отбор) = Тип("Массив") Тогда
		ПараметрыРегистрации.Вставить("РегистрируемыеОбработчики", Отбор);
		Для Каждого Обработчик Из Отбор Цикл
			СброситьСостояниеОбработчика(Обработчик);
			
			СведенияОбОбработчике = СведенияОБлокируемыхОбъектах.Обработчики[Обработчик];
			Если СведенияОбОбработчике <> Неопределено Тогда
				СведенияОБлокируемыхОбъектах.Обработчики[Обработчик].Выполнен = Ложь;
			КонецЕсли;
		КонецЦикла;
		
		НезавершенныеОбработчики = Запрос.Выполнить().Выгрузить();
		Для Каждого НезавершенныйОбработчик Из НезавершенныеОбработчики Цикл
			СброситьСостояниеОбработчика(НезавершенныйОбработчик.ИмяОбработчика);
			
			СведенияОбОбработчике = СведенияОБлокируемыхОбъектах.Обработчики[Обработчик];
			Если СведенияОбОбработчике <> Неопределено Тогда
				СведенияОБлокируемыхОбъектах.Обработчики[Обработчик].Выполнен = Ложь;
			КонецЕсли;
		КонецЦикла;
	ИначеЕсли ТипЗнч(Отбор) = Тип("Структура") Тогда
		ИтерацииОбновления = ОбновлениеИнформационнойБазыСлужебный.ИтерацииОбновления();
		Для Каждого ИтерацияОбновления Из ИтерацииОбновления Цикл
			Если Отбор.Свойство(ИтерацияОбновления.Подсистема) Тогда
				ИтерацияОбновления.ПредыдущаяВерсия = Отбор[ИтерацияОбновления.Подсистема];
			КонецЕсли;
		КонецЦикла;
		
		Обработки.ОписаниеОбработчиковОбновления.ЗаполнитьНомерОчереди(ИтерацииОбновления);
		ОбновлениеИнформационнойБазыСлужебный.ОбновитьСписокВыполняемыхОбработчиковОбновления(ИтерацииОбновления, Ложь, "Отложенные");
		
	КонецЕсли;
	
	ОбновлениеИнформационнойБазыСлужебный.ЗаписатьСведенияОБлокируемыхОбъектах(СведенияОБлокируемыхОбъектах);
	ОбновлениеИнформационнойБазыСлужебный.ЗаполнитьДанныеДляПараллельногоОтложенногоОбновления(ПараметрыРегистрации);
	
	// Повторное получение временных очередей, если они были добавлены.
	ВременныеОчереди = ЗапросВременных.Выполнить().Выгрузить();
	
	// Перенос очередей.
	БлокировкаДанных = Новый БлокировкаДанных;
	ЭлементБлокировки = БлокировкаДанных.Добавить("ПланОбмена.ОбновлениеИнформационнойБазы");
	ЭлементБлокировки.ИсточникДанных = ВременныеОчереди;
	ЭлементБлокировки.ИспользоватьИзИсточникаДанных("Ссылка", "Ссылка");
	НачатьТранзакцию();
	Попытка
		БлокировкаДанных.Заблокировать();
		
		Для Каждого ВременнаяОчередь Из ВременныеОчереди Цикл
			ВременнаяОчередьОбъект = ВременнаяОчередь.Ссылка.ПолучитьОбъект();
			Очередь = ВременнаяОчередьОбъект.Очередь;
			
			ОсновнаяОчередь = ОчередьСсылкой(Очередь);
			ОсновнаяОчередьОбъект = ОсновнаяОчередь.ПолучитьОбъект();
			
			ВременнаяОчередьОбъект.Наименование = XMLСтрока(Очередь);
			ВременнаяОчередьОбъект.Временная = Ложь;
			
			ОсновнаяОчередьОбъект.Наименование = XMLСтрока(Очередь) + " " + НСтр("ru = 'Старая после перезапуска'");
			ОсновнаяОчередьОбъект.Временная = Истина;
			
			ВременнаяОчередьОбъект.Записать();
			ОсновнаяОчередьОбъект.Записать();
			
			ПланыОбмена.УдалитьРегистрациюИзменений(ОсновнаяОчередь);
		КонецЦикла;
		
		ЗафиксироватьТранзакцию();
	Исключение
		ОтменитьТранзакцию();
		ВызватьИсключение;
	КонецПопытки;
	
	СведенияОбОбновлении = ОбновлениеИнформационнойБазыСлужебный.СведенияОбОбновленииИнформационнойБазы();
	СведенияОбОбновлении.ОтложенноеОбновлениеЗавершеноУспешно = Неопределено;
	СведенияОбОбновлении.ВремяОкончаниеОтложенногоОбновления = Неопределено;
	СведенияОбОбновлении.ТекущаяИтерацияОбновления = 1;
	ОбновлениеИнформационнойБазыСлужебный.ЗаписатьСведенияОбОбновленииИнформационнойБазы(СведенияОбОбновлении);
	
	Константы.ПорядокОбрабатываемыхДанных.Установить(Перечисления.ПорядокОбработчиковОбновления.Критичный);
	
	Константы.ОтложенноеОбновлениеЗавершеноУспешно.Установить(Ложь);
	Если Не ОбщегоНазначения.ЭтоПодчиненныйУзелРИБ() Тогда
		Константы.ОтложенноеОбновлениеВГлавномУзлеЗавершеноУспешно.Установить(Ложь);
	КонецЕсли;
	
	ОбновлениеИнформационнойБазыСлужебный.ПриВключенииОтложенногоОбновления(Истина);
	
КонецПроцедуры

// Выполняет перезапуск монопольных и оперативных обработчиков обновления в базе, где уже выполнилась
// монопольная (оперативная) часть обновления.
//
// Параметры:
//  Отбор - Структура:
//    * Ключ     - Строка - имя подсистемы.
//    * Значение - Строка - номер версии.
//
Процедура ПерезапуститьМонопольноеОбновление(Отбор) Экспорт
	
	ИтерацииОбновления = ОбновлениеИнформационнойБазыСлужебный.ИтерацииОбновления();
	ВерсииУстановлены = Ложь;
	Для Каждого ИтерацияОбновления Из ИтерацииОбновления Цикл
		Если Отбор.Свойство(ИтерацияОбновления.Подсистема) Тогда
			ИтерацияОбновления.ПредыдущаяВерсия = Отбор[ИтерацияОбновления.Подсистема];
			ВерсииУстановлены = Истина;
		КонецЕсли;
	КонецЦикла;
	
	Если Не ВерсииУстановлены Тогда
		Возврат;
	КонецЕсли;
	
	Обработки.ОписаниеОбработчиковОбновления.ЗаполнитьНомерОчереди(ИтерацииОбновления);
	ОбновлениеИнформационнойБазыСлужебный.ОбновитьСписокВыполняемыхОбработчиковОбновления(ИтерацииОбновления, Ложь, "Монопольные");
	
	ХодВыполненияОбработчиков = Новый Структура;
	ХодВыполненияОбработчиков.Вставить("ВсегоОбработчиков", 0);
	ХодВыполненияОбработчиков.Вставить("ВыполненоОбработчиков", 0);
	
	Параметры = Новый Структура;
	Параметры.Вставить("ХодВыполненияОбработчиков", ХодВыполненияОбработчиков);
	Параметры.Вставить("ОперативноеОбновление", Ложь);
	Параметры.Вставить("ВФоне", Ложь);
	Параметры.Вставить("ОтметитьРегистрациюДанных", Истина);
	
	Для Каждого ИтерацияОбновления Из ИтерацииОбновления Цикл
		ОбновлениеИнформационнойБазыСлужебный.ВыполнитьИтерациюОбновления(ИтерацияОбновления, Параметры);
	КонецЦикла;
	
КонецПроцедуры

// Регистрирует проблему с данными, выявленную при выполнении обработчика обновления, в подсистеме КонтрольВеденияУчета.
//
// Параметры:
//  ПроблемныйОбъект - ЛюбаяСсылка - объект, в котором обнаружена проблема.
//  УточнениеПроблемы - Строка - описание выявленной проблемы.
//  Параметры - Структура - параметры, переданные на вход обработчика обновления.
//
Процедура ЗарегистрироватьПроблемуСДанными(ПроблемныйОбъект, УточнениеПроблемы, Параметры = Неопределено) Экспорт
	
	Если Не ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.КонтрольВеденияУчета") Тогда
		Возврат;
	КонецЕсли;
	
	Если ТипЗнч(Параметры) = Тип("Структура")
		И Параметры.Свойство("ИмяОбработчика") Тогда
		ИмяОбработчика = Параметры.ИмяОбработчика;
	Иначе
		ИмяОбработчика = ПараметрыСеанса.ПараметрыОбработчикаОбновления.ИмяОбработчика;
	КонецЕсли;
	
	МодульКонтрольВеденияУчета          = ОбщегоНазначения.ОбщийМодуль("КонтрольВеденияУчета");
	МодульКонтрольВеденияУчетаСлужебный = ОбщегоНазначения.ОбщийМодуль("КонтрольВеденияУчетаСлужебный");
	
	ПараметрыВыполненияПроверки = МодульКонтрольВеденияУчета.ПараметрыВыполненияПроверки("ОбновлениеВерсииИБ", ИмяОбработчика);
	ВидПроверки = МодульКонтрольВеденияУчета.ВидПроверки(ПараметрыВыполненияПроверки);
	Проверка    = МодульКонтрольВеденияУчета.ПроверкаПоИдентификатору("ОбновлениеИнформационнойБазыПроблемаСДанными");
	
	ПараметрыПроверки = МодульКонтрольВеденияУчетаСлужебный.ПодготовитьПараметрыПроверки(Проверка, Неопределено);
	
	Проблема = МодульКонтрольВеденияУчета.ОписаниеПроблемы(ПроблемныйОбъект, ПараметрыПроверки);
	Проблема.ВидПроверки = ВидПроверки;
	Проблема.УточнениеПроблемы = УточнениеПроблемы;
	
	ШаблонЗаписиВЖурнал = НСтр("ru = 'При выполнении обработчика ""%1"" обнаружена проблема с данными в объекте ""%2"".
		|Уточнение проблемы:
		|%3'");
	
	ТекстДляЗаписиВЖурнал = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(ШаблонЗаписиВЖурнал,
		ИмяОбработчика, ПроблемныйОбъект, УточнениеПроблемы);
	ОбновлениеИнформационнойБазыСлужебный.ЗаписатьПредупреждение(ТекстДляЗаписиВЖурнал);
	
	МодульКонтрольВеденияУчета.ЗаписатьПроблему(Проблема, ПараметрыПроверки);
	
КонецПроцедуры

// Позволяет включить или отключить отложенное обновление. В коробке управляет флагом Использование
// у регламентного задания ОтложенноеОбновлениеИБ, в в модели сервиса заданием очереди.
//
// Параметры:
//  Использование - Булево - Истина, если требуется включить отложенное обновление.
//
Процедура ВключитьОтключитьОтложенноеОбновление(Использование) Экспорт
	
	ОбновлениеИнформационнойБазыСлужебный.ПриВключенииОтложенногоОбновления(Использование);
	
КонецПроцедуры

#КонецОбласти

#Область СлужебныеПроцедурыИФункции

Процедура ДобавитьПроверкуБлокировкиДополнительныхИсточников(Очередь, ТекстЗапроса, ПолноеИмяОбъекта, ПолноеИмяРегистра, МенеджерВременныхТаблиц, ЭтоСозданиеВременнойТаблицы, ДополнительныеПараметры)
	
	Если ДополнительныеПараметры.ДополнительныеИсточникиДанных.Количество() = 0 Тогда
		ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&УсловиеПоДопИсточникамСсылкам", "ИСТИНА");
		ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&УсловиеПоДопИсточникамРегистрам", "ИСТИНА");
	Иначе
		ДополнительныеИсточникиСсылки = Новый Массив;
		ДополнительныеИсточникиРегистры = Новый Массив;
		
		Для Каждого КлючЗначение Из ДополнительныеПараметры.ДополнительныеИсточникиДанных Цикл
			ИсточникДанных = КлючЗначение.Ключ;
			
			Если СтандартныеПодсистемыСервер.ЭтоТаблицаРегистра(ИсточникДанных) И СтрНайти(ИсточникДанных,".") <> 0 Тогда
				ДополнительныеИсточникиРегистры.Добавить(ИсточникДанных);
			Иначе
				ДополнительныеИсточникиСсылки.Добавить(ИсточникДанных);
			КонецЕсли;
		КонецЦикла;
		
		#Область ДополнительныеИсточникиСсылки
		
		Если ДополнительныеИсточникиСсылки.Количество() > 0 Тогда
			Если ПолноеИмяОбъекта = Неопределено Тогда
				ТекстИсключения = НСтр("ru = 'Ошибка вызова функции %ИмяФункции%: не передано имя документа, но переданы дополнительные источники данных.'");
				ТекстИсключения = СтрЗаменить(ТекстИсключения, "%ИмяФункции%", "ОбновлениеИнформационнойБазы.ДобавитьПроверкуБлокировкиДополнительныхИсточников");
				ВызватьИсключение ТекстИсключения;
			КонецЕсли;
			
			МетаданныеДокумента = ОбщегоНазначения.ОбъектМетаданныхПоПолномуИмени(ПолноеИмяОбъекта);
			ВременныеТаблицыЗаблокированныхДопИсточников = Новый Соответствие;
			РеквизитыШапки = Новый Соответствие;
			РеквизитыТЧ = Новый Соответствие;
			
			Для Каждого ИсточникДанных Из ДополнительныеИсточникиСсылки Цикл
				СоставИсточникаДанных = СоставСсылочногоИсточникаДанных(ИсточникДанных);
				ИмяТЧ = СоставИсточникаДанных.ТабличнаяЧасть;
				ИмяРеквизита = СоставИсточникаДанных.Реквизит;
				
				Если ЗначениеЗаполнено(ИмяТЧ) Тогда
					ТипыИсточника = МетаданныеДокумента.ТабличныеЧасти[ИмяТЧ].Реквизиты[ИмяРеквизита].Тип.Типы();
				Иначе
					ТипыИсточника = МетаданныеДокумента.Реквизиты[ИмяРеквизита].Тип.Типы();
				КонецЕсли;
				
				Для Каждого ТипИсточника Из ТипыИсточника Цикл
					Если ЭтоПримитивныйТип(ТипИсточника) Или ЭтоПеречисление(ТипИсточника) Тогда
						Продолжить;
					КонецЕсли;
					
					МетаданныеИсточника = Метаданные.НайтиПоТипу(ТипИсточника);
					
					Если ЗначениеЗаполнено(ИмяТЧ) Тогда
						МетаданныеРеквизитов = ЗначениеПоКлючу(РеквизитыТЧ, ИмяТЧ);
						Реквизиты = ЗначениеПоКлючу(МетаданныеРеквизитов, МетаданныеИсточника);
						Реквизиты[ИмяРеквизита] = Истина;
					Иначе
						Реквизиты = ЗначениеПоКлючу(РеквизитыШапки, МетаданныеИсточника);
						Реквизиты[ИмяРеквизита] = Истина;
					КонецЕсли;
					
					ИмяВТЗаблокированногоДопИсточника = ВременныеТаблицыЗаблокированныхДопИсточников[МетаданныеИсточника];
					
					Если ИмяВТЗаблокированногоДопИсточника = Неопределено Тогда
						ПолноеИмяИсточника = МетаданныеИсточника.ПолноеИмя();
						ИмяВТЗаблокированногоДопИсточника = "ВТЗаблокировано" + СтрЗаменить(ПолноеИмяИсточника,".","_");
						ВременныеТаблицыЗаблокированныхДопИсточников[МетаданныеИсточника] = ИмяВТЗаблокированногоДопИсточника;
						ДополнительныеПараметрыСозданияВТ = ДополнительныеПараметрыВыборкиДанныхДляОбработки();
						ДополнительныеПараметрыСозданияВТ.ИмяВременнойТаблицы = ИмяВТЗаблокированногоДопИсточника;
						СоздатьВременнуюТаблицуЗаблокированныхДляЧтенияИИзмененияДанных(Очередь, // @skip-check query-in-loop - пакетная проверка заблокированных данных.
							ПолноеИмяИсточника,
							МенеджерВременныхТаблиц,
							ДополнительныеПараметрыСозданияВТ); 
					КонецЕсли;
				КонецЦикла;
			КонецЦикла;
			
			УсловияПоДопИсточникамСсылкам = Новый Массив;
			РазделительУсловийПоДопИсточникамСсылкам =
				"
				|	И ";
			
			Если РеквизитыТЧ.Количество() > 0 Тогда
				Запрос = Новый Запрос;
				Запрос.Текст = ТекстЗапросаВТЗаблокированныхПоТЧ(РеквизитыТЧ,
					ПолноеИмяОбъекта,
					ПолноеИмяРегистра,
					ВременныеТаблицыЗаблокированныхДопИсточников);
				Запрос.МенеджерВременныхТаблиц = МенеджерВременныхТаблиц;
				Запрос.Выполнить();
				
				УсловияПоДопИсточникамСсылкам.Добавить(ТекстУсловияЗаблокированныхПоТЧ(ПолноеИмяОбъекта, ПолноеИмяРегистра));
				ВременныеТаблицыЗаблокированныхДопИсточников["ЗаблокированныеПоТЧ"] = "ЗаблокированныеПоТЧ";
			КонецЕсли;
			
			Если РеквизитыШапки.Количество() > 0 Тогда
				УсловияЗаблокированныхПоШапке = ТекстУсловияЗаблокированныхПоШапке(РеквизитыШапки,
					ПолноеИмяРегистра,
					ВременныеТаблицыЗаблокированныхДопИсточников);
				УсловияПоДопИсточникамСсылкам.Добавить(УсловияЗаблокированныхПоШапке);
			КонецЕсли;
			
			УсловиеПоДопИсточникамСсылкам = СтрСоединить(УсловияПоДопИсточникамСсылкам,
				РазделительУсловийПоДопИсточникамСсылкам);
			
			ТекстыЗапроса = Новый Массив;
			ТекстыЗапроса.Добавить(ТекстЗапроса);
			ДобавитьЗапросыУничтоженияВТ(ТекстыЗапроса, ВременныеТаблицыЗаблокированныхДопИсточников);
			ТекстЗапроса = ОбъединитьЗапросыВПакет(ТекстыЗапроса);
			
			ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&УсловиеПоДопИсточникамСсылкам", УсловиеПоДопИсточникамСсылкам);
			ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "#ПолноеИмяДокумента", ПолноеИмяОбъекта);
		Иначе
			ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&УсловиеПоДопИсточникамСсылкам", "ИСТИНА");
		КонецЕсли;
		#КонецОбласти
		
		#Область ДополнительныеИсточникиРегистры

		Если ДополнительныеИсточникиРегистры.Количество() > 0 Тогда
			
			УсловиеПоДопИсточникамРегистрам = "ИСТИНА";
			
			ВременныеТаблицыЗаблокированныхДопИсточников = Новый Соответствие;
			
			Для Каждого ИсточникДанных Из ДополнительныеИсточникиРегистры Цикл
				МетаданныеИсточника = ОбщегоНазначения.ОбъектМетаданныхПоПолномуИмени(ИсточникДанных);
				
				Если ОбщегоНазначения.ЭтоРегистрСведений(МетаданныеИсточника)
					И МетаданныеИсточника.РежимЗаписи = Метаданные.СвойстваОбъектов.РежимЗаписиРегистра.Независимый Тогда
					
					ТекстИсключения = НСтр("ru = 'Регистр %ИсточникДанных% независимый. Поддерживается проверка только по регистрам, подчиненным регистраторам.'");
					ТекстИсключения = СтрЗаменить(ТекстИсключения, "%ИсточникДанных%",ИсточникДанных);
					ВызватьИсключение ТекстИсключения;
				КонецЕсли;
				
				ИмяВТЗаблокированногоДопИсточника = ВременныеТаблицыЗаблокированныхДопИсточников.Получить(МетаданныеИсточника);
				
				Если ИмяВТЗаблокированногоДопИсточника = Неопределено Тогда
					ИмяВТЗаблокированногоДопИсточника = "ВТЗаблокировано" + СтрЗаменить(ИсточникДанных,".","_");
					
					ДополнительныеПараметрыСозданияВТ = ДополнительныеПараметрыВыборкиДанныхДляОбработки();
					ДополнительныеПараметрыСозданияВТ.ИмяВременнойТаблицы = ИмяВТЗаблокированногоДопИсточника;
					СоздатьВременнуюТаблицуЗаблокированныхДляЧтенияИИзмененияДанных(Очередь, ИсточникДанных, МенеджерВременныхТаблиц, ДополнительныеПараметрыСозданияВТ); // @skip-check query-in-loop - пакетная проверка заблокированных данных.
					
					ВременныеТаблицыЗаблокированныхДопИсточников.Вставить(МетаданныеИсточника, ИмяВТЗаблокированногоДопИсточника);
				КонецЕсли;
			КонецЦикла;
			
			ТекстыЗапроса = Новый Массив;
			ТекстыЗапроса.Добавить(ТекстЗапроса);
			ДобавитьЗапросыУничтоженияВТ(ТекстыЗапроса, ВременныеТаблицыЗаблокированныхДопИсточников);
			ТекстЗапроса = ОбъединитьЗапросыВПакет(ТекстыЗапроса);
			
			УсловиеПоДопИсточникамРегистрам = ТекстУсловияЗаблокированныхПоРегистрам(ПолноеИмяРегистра,
				ВременныеТаблицыЗаблокированныхДопИсточников);
			ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&УсловиеПоДопИсточникамРегистрам", УсловиеПоДопИсточникамРегистрам);
		Иначе
			ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&УсловиеПоДопИсточникамРегистрам", "ИСТИНА");
		КонецЕсли;
		#КонецОбласти
	КонецЕсли;	
КонецПроцедуры

Функция ЭтоПримитивныйТип(ПроверяемыйТип)
	
	Если ПроверяемыйТип = Тип("Неопределено")
		Или ПроверяемыйТип = Тип("Булево")
		Или ПроверяемыйТип = Тип("Строка")
		Или ПроверяемыйТип = Тип("Число")
		Или ПроверяемыйТип = Тип("Дата")
		Или ПроверяемыйТип = Тип("УникальныйИдентификатор") Тогда
		
		Возврат Истина;
		
	Иначе
		
		Возврат Ложь;
		
	КонецЕсли;
	
КонецФункции

Процедура ДобавитьПроверкуБлокировкиДополнительныхИсточниковДляНезависимогоРегистра(Очередь, ТекстЗапроса, ПолноеИмяРегистра, МенеджерВременныхТаблиц, ДополнительныеПараметры)
	
	Если ДополнительныеПараметры.ДополнительныеИсточникиДанных.Количество() = 0 Тогда
		
		ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "#ТекстЗапросаСоединениеСДопИсточниками", "");
		ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&УсловиеПоДопИсточникамСсылкам", "ИСТИНА");
	
	Иначе
		
		МетаданныеРегистра = ОбщегоНазначения.ОбъектМетаданныхПоПолномуИмени(ПолноеИмяРегистра);
		УсловияПоДопИсточникамСсылкам = Новый Массив;
		ШаблонУсловияПоДопИсточникамСсылкам =
			"	НЕ ИСТИНА В (
			|		ВЫБРАТЬ ПЕРВЫЕ 1
			|			ИСТИНА
			|		ИЗ
			|			%1 КАК %1
			|		ГДЕ
			|			ТаблицаИзменений.%2 = %1.Ссылка)";
		
		Для Каждого КлючЗначение Из ДополнительныеПараметры.ДополнительныеИсточникиДанных Цикл
			
			ИсточникДанных = КлючЗначение.Ключ;
			
			ТипыИсточника = МетаданныеРегистра.Измерения[ИсточникДанных].Тип.Типы();
			МассивОбъектовМетаданных = Новый Массив;
			
			Для Каждого ТипИсточника Из ТипыИсточника Цикл
				
				Если ЭтоПримитивныйТип(ТипИсточника) Или ЭтоПеречисление(ТипИсточника) Тогда
					Продолжить;
				КонецЕсли;
				
				МассивОбъектовМетаданных.Добавить(Метаданные.НайтиПоТипу(ТипИсточника));
				
			КонецЦикла;
			
			ДополнительныеПараметрыСозданияВТ = ДополнительныеПараметрыВыборкиДанныхДляОбработки();
			ИмяВременнойТаблицы = "ВТЗаблокировано" + ИсточникДанных;
			ДополнительныеПараметрыСозданияВТ.ИмяВременнойТаблицы = ИмяВременнойТаблицы;
			
			СоздатьВременнуюТаблицуЗаблокированныхДляЧтенияИИзмененияСсылок(Очередь, МассивОбъектовМетаданных, МенеджерВременныхТаблиц, ДополнительныеПараметрыСозданияВТ); // @skip-check query-in-loop - пакетная проверка заблокированных данных.
			
			УсловиеПоДопИсточникамСсылкам = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				ШаблонУсловияПоДопИсточникамСсылкам,
				ИмяВременнойТаблицы,
				ИсточникДанных);
			УсловияПоДопИсточникамСсылкам.Добавить(УсловиеПоДопИсточникамСсылкам);
		КонецЦикла;
		
		РазделительИ =
			"
			|	И ";
		УсловиеПоДопИсточникамСсылкам = СтрСоединить(УсловияПоДопИсточникамСсылкам, РазделительИ);
		ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&УсловиеПоДопИсточникамСсылкам", УсловиеПоДопИсточникамСсылкам);

	КонецЕсли;
КонецПроцедуры

Процедура УстановитьНедостающиеОтборыВНаборе(Набор, МетаданныеНабора, УстанавливаемыеОтборы)
	Для Каждого Измерение Из МетаданныеНабора.Измерения Цикл
		
		ЕстьОтборПоИзмерению = Ложь;
		
		Если ТипЗнч(УстанавливаемыеОтборы) = Тип("ТаблицаЗначений") Тогда
			ЕстьОтборПоИзмерению = УстанавливаемыеОтборы.Колонки.Найти(Измерение.Имя) <> Неопределено;
		Иначе //Отбор
			ЕстьОтборПоИзмерению = УстанавливаемыеОтборы[Измерение.Имя].Использование;	
		КонецЕсли;
		
		Если Не ЕстьОтборПоИзмерению Тогда
			ПустоеЗначение = Измерение.Тип.ПривестиЗначение();
			Набор.Отбор[Измерение.Имя].Установить(ПустоеЗначение);
		КонецЕсли;
	КонецЦикла;
	
	Если МетаданныеНабора.ОсновнойОтборПоПериоду Тогда
		
		Если ТипЗнч(УстанавливаемыеОтборы) = Тип("ТаблицаЗначений") Тогда
			ЕстьОтборПоИзмерению = УстанавливаемыеОтборы.Колонки.Найти("Период") <> Неопределено;
		Иначе //Отбор
			ЕстьОтборПоИзмерению = УстанавливаемыеОтборы.Период.Использование;
		КонецЕсли;
		
		Если Не ЕстьОтборПоИзмерению Тогда
			ПустоеЗначение = '00010101';
			Набор.Отбор.Период.Установить(ПустоеЗначение);
		КонецЕсли;
		
	КонецЕсли;
КонецПроцедуры

// Зарегистрировать изменения одного элемента данных, как это было до оптимизации.
//
// Параметры:
//  Параметры - см. ОбновлениеИнформационнойБазы.ОсновныеПараметрыОтметкиКОбработке.
//  Узел - ПланОбменаСсылка.ОбновлениеИнформационнойБазы.
//  Данные - ЛюбаяСсылка
//         - РегистрСведенийНаборЗаписей
//         - РегистрНакопленияНаборЗаписей
//         - РегистрБухгалтерииНаборЗаписей
//         - РегистрРасчетаНаборЗаписей.
//  ВидДанных - Строка.
//  ПолноеИмяОбъекта - Строка.
//
Процедура ЗарегистрироватьИзменения(Параметры, Узел, Данные, ВидДанных, ПолноеИмяОбъекта = "")
	
	ПараметрыРегистрации = НовыеПараметрыРегистрации(Параметры, Узел, ВидДанных, ПолноеИмяОбъекта);
	ЗарегистрироватьИзмененияЭлементаДанных(Данные, ПараметрыРегистрации);
	ЗавершитьРегистрациюПорцииДанных(ПараметрыРегистрации);
	
КонецПроцедуры

// Зарегистрировать изменения одной или нескольких ссылок.
//
// Параметры:
//  Параметры - см. ОбновлениеИнформационнойБазы.ОсновныеПараметрыОтметкиКОбработке.
//  Узел - ПланОбменаСсылка.ОбновлениеИнформационнойБазы.
//  Ссылки - ЛюбаяСсылка
//         - Массив из ЛюбаяСсылка.
//  ВидДанных - Строка.
//  ПолноеИмяОбъекта - Строка.
//
Процедура ЗарегистрироватьИзмененияОбъекта(Параметры, Узел, Ссылки, ВидДанных, ПолноеИмяОбъекта = "")
	
	ПараметрыРегистрации = НовыеПараметрыРегистрации(Параметры, Узел, ВидДанных, ПолноеИмяОбъекта);
	
	Для Каждого Ссылка Из МассивЭлементов(Ссылки) Цикл
		Если Ссылка.Пустая() Тогда
			Продолжить;
		КонецЕсли;
		
		ЗарегистрироватьИзмененияЭлементаДанных(Ссылка, ПараметрыРегистрации);
	КонецЦикла;
	
	ЗавершитьРегистрациюПорцииДанных(ПараметрыРегистрации);
	
КонецПроцедуры

// Зарегистрировать изменения подчиненного регистра по указанным регистраторам.
//
// Параметры:
//  Параметры - см. ОбновлениеИнформационнойБазы.ОсновныеПараметрыОтметкиКОбработке.
//  Узел - ПланОбменаСсылка.ОбновлениеИнформационнойБазы.
//  Регистраторы - ЛюбаяСсылка
//               - Массив из ЛюбаяСсылка.
//  ВидДанных - Строка.
//  ПолноеИмяОбъекта - Строка.
//
Процедура ЗарегистрироватьИзмененияПодчиненногоРегистра(Параметры, Узел, Регистраторы, ВидДанных, ПолноеИмяОбъекта)
	
	ПараметрыРегистрации = НовыеПараметрыРегистрации(Параметры, Узел, ВидДанных, ПолноеИмяОбъекта);
	
	Для Каждого Регистратор Из МассивЭлементов(Регистраторы) Цикл
		Набор = ПолучитьПереиспользуемыйНабор(ПараметрыРегистрации.ПереиспользуемыеНаборы);
		Набор.Отбор.Регистратор.Установить(Регистратор);
		
		ЗарегистрироватьИзмененияЭлементаДанных(Набор, ПараметрыРегистрации);
	КонецЦикла;
	
	ЗавершитьРегистрациюПорцииДанных(ПараметрыРегистрации);
	
КонецПроцедуры

// Зарегистрировать изменения независимого регистра по таблице значений,
// где колонки - это измерения, а строки - это регистрируемые записи.
//
// Параметры:
//  Параметры - см. ОбновлениеИнформационнойБазы.ОсновныеПараметрыОтметкиКОбработке.
//  Узел - ПланОбменаСсылка.ОбновлениеИнформационнойБазы.
//  Записи - ТаблицаЗначений.
//  ВидДанных - Строка.
//  ПолноеИмяОбъекта - Строка.
//
Процедура ЗарегистрироватьИзмененияНезависимогоРегистра(Параметры, Узел, Записи, ВидДанных, ПолноеИмяОбъекта)
	
	ПараметрыРегистрации = НовыеПараметрыРегистрации(Параметры, Узел, ВидДанных, ПолноеИмяОбъекта);
	МетаданныеРегистра = ОбщегоНазначения.ОбъектМетаданныхПоПолномуИмени(ПолноеИмяОбъекта);
	
	Для Каждого Запись Из Записи Цикл
		Набор = ПолучитьПереиспользуемыйНабор(ПараметрыРегистрации.ПереиспользуемыеНаборы);
		УстановитьНедостающиеОтборыВНаборе(Набор, МетаданныеРегистра, Записи);
		
		Для Каждого Колонка Из Записи.Колонки Цикл
			Набор.Отбор[Колонка.Имя].Значение = Запись[Колонка.Имя];
			Набор.Отбор[Колонка.Имя].Использование = Истина;
		КонецЦикла;
		
		ЗарегистрироватьИзмененияЭлементаДанных(Набор, ПараметрыРегистрации);
	КонецЦикла;
	
	ЗавершитьРегистрациюПорцииДанных(ПараметрыРегистрации);
	
КонецПроцедуры

// Зарегистрировать элемент данных в таблице регистрации изменений,
// увеличить счетчик зарегистрированных данных
// и записать данные для обновления в файл рИБ (необходимо в случае РИБ с фильтрами).
//
// Параметры:
//  Данные - ЛюбаяСсылка
//         - РегистрСведенийНаборЗаписей
//         - РегистрНакопленияНаборЗаписей
//         - РегистрБухгалтерииНаборЗаписей
//         - РегистрРасчетаНаборЗаписей.
//  ПараметрыРегистрации - см. НовыеПараметрыРегистрации.
//
Процедура ЗарегистрироватьИзмененияЭлементаДанных(Данные, ПараметрыРегистрации)
	
	Если ПараметрыРегистрации.ДоступнаПакетнаяРегистрация Тогда
		ПараметрыРегистрации.ДанныеДляРегистрации.Добавить(Данные);
		ЗарегистрироватьПакетДанных(ПараметрыРегистрации, ПараметрыРегистрации.ДанныеДляРегистрации);
	Иначе
		Попытка
			ПланыОбмена.ЗарегистрироватьИзменения(ПараметрыРегистрации.Узел, Данные);
		Исключение
			ТекстИсключения = НСтр("ru = 'Не удалось зарегистрировать данные для обработки. Возможно, таблицы, по которым
				|выполняется регистрация данных, не включены в состав плана обмена ""%1"".
				|Для выявления таких ошибок рекомендуется использовать инструмент ""%2"".
				|
				|%3'");
			ТекстИсключения = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(ТекстИсключения, 
				Метаданные.ПланыОбмена.ОбновлениеИнформационнойБазы.Имя,
				"ПроверкаВнедренияБСП",
				ОбработкаОшибок.ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
			ВызватьИсключение ТекстИсключения;
		КонецПопытки;
		ОсвободитьПереиспользуемыеНаборыВПараметрах(ПараметрыРегистрации);
	КонецЕсли;
	
	УвеличитьКоличествоЗарегистрированныхДанных(ПараметрыРегистрации.Параметры,
		Данные,
		ПараметрыРегистрации.ПолноеИмяОбъекта);
	
	Если ПараметрыРегистрации.ЗаписыватьДанныеДляОбновленияВФайл Тогда
		ПараметрыРегистрации.МодульОбменДаннымиСервер.ЗаписатьДанныеДляОбновленияВФайл(ПараметрыРегистрации.Параметры,
			Данные,
			ПараметрыРегистрации.ВидДанных,
			ПараметрыРегистрации.ПолноеИмяОбъекта);
	КонецЕсли;
	
КонецПроцедуры

// Зарегистрировать пакет данных, если он уже достаточно большой
// или это нужно сделать принудительно в конце регистрации порции данных.
//
// Параметры:
//  ПараметрыРегистрации - см. НовыеПараметрыРегистрации.
//  Данные - Массив из ЛюбаяСсылка
//         - Массив из РегистрСведенийНаборЗаписей
//         - Массив из РегистрНакопленияНаборЗаписей
//         - Массив из РегистрБухгалтерииНаборЗаписей
//         - Массив из РегистрРасчетаНаборЗаписей.
//  Принудительно - Булево - Истина, если нужно зарегистрировать, даже если пакет мал.
//  РазмерПакета - Число - если в пакете столько же или больше записей, то он записывается.
//
Процедура ЗарегистрироватьПакетДанных(ПараметрыРегистрации, Данные, Принудительно = Ложь, РазмерПакета = 1000)
	
	Если Данные.Количество() >= РазмерПакета Или Принудительно И Данные.Количество() > 0 Тогда
		Попытка
			ПланыОбмена.ЗарегистрироватьИзменения(ПараметрыРегистрации.Узел, Данные);
		Исключение
			ТекстИсключения = НСтр("ru = 'Не удалось зарегистрировать данные для обработки. Возможно, таблицы, по которым
				|выполняется регистрация данных, не включены в состав плана обмена ""%1"".
				|Для выявления таких ошибок рекомендуется использовать инструмент ""%2"".
				|
				|%3'");
			ТекстИсключения = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(ТекстИсключения, 
				Метаданные.ПланыОбмена.ОбновлениеИнформационнойБазы.Имя,
				"ПроверкаВнедренияБСП",
				ОбработкаОшибок.ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
			ВызватьИсключение ТекстИсключения;
		КонецПопытки;
		Данные.Очистить();
		
		ОсвободитьПереиспользуемыеНаборыВПараметрах(ПараметрыРегистрации);
	КонецЕсли;
	
КонецПроцедуры

// Записать оставшуюся порцию данных в конце процесса регистрации, если доступна пакетная регистрация.
//
// Параметры:
//  ПараметрыРегистрации - см. НовыеПараметрыРегистрации.
//
Процедура ЗавершитьРегистрациюПорцииДанных(ПараметрыРегистрации)
	
	Если ПараметрыРегистрации.ДоступнаПакетнаяРегистрация Тогда
		ЗарегистрироватьПакетДанных(ПараметрыРегистрации, ПараметрыРегистрации.ДанныеДляРегистрации, Истина);
	КонецЕсли;
	
КонецПроцедуры

// Увеличить количество зарегистрированных данных в статистике.
//
// Параметры:
//  Параметры - см. ОбновлениеИнформационнойБазы.ОсновныеПараметрыОтметкиКОбработке.
//  Данные - ЛюбаяСсылка
//         - РегистрСведенийНаборЗаписей
//         - РегистрНакопленияНаборЗаписей
//         - РегистрБухгалтерииНаборЗаписей
//         - РегистрРасчетаНаборЗаписей.
//  ПолноеИмяОбъекта - Строка.
//
Процедура УвеличитьКоличествоЗарегистрированныхДанных(Параметры, Данные, ПолноеИмяОбъекта)
	
	Если Параметры.Свойство("ДанныеОбработчика") Тогда
		Если Не ЗначениеЗаполнено(ПолноеИмяОбъекта) Тогда
			ПолноеИмя = Данные.Метаданные().ПолноеИмя();
		Иначе
			ПолноеИмя = ПолноеИмяОбъекта;
		КонецЕсли;
		
		ДанныеПоОбъекту = Параметры.ДанныеОбработчика[ПолноеИмя];
		
		Если ДанныеПоОбъекту = Неопределено Тогда
			ДанныеПоОбъекту = Новый Структура;
			ДанныеПоОбъекту.Вставить("Количество", 1);
			ДанныеПоОбъекту.Вставить("Очередь", Параметры.Очередь);
			Параметры.ДанныеОбработчика.Вставить(ПолноеИмя, ДанныеПоОбъекту);
		Иначе
			ДанныеПоОбъекту.Количество = ДанныеПоОбъекту.Количество + 1;
		КонецЕсли;
	КонецЕсли;
	
КонецПроцедуры

// Удалить регистрацию изменений одной или нескольких ссылок.
//
// Параметры:
//  Узел - ПланОбменаСсылка.ОбновлениеИнформационнойБазы.
//  Ссылки - ЛюбаяСсылка
//         - Массив из ЛюбаяСсылка.
//
Процедура УдалитьРегистрациюИзмененийОбъекта(Узел, Ссылки)
	
	ПараметрыУдаления = НовыеПараметрыУдаленияРегистрации(Узел);
	
	Для Каждого Ссылка Из МассивЭлементов(Ссылки) Цикл
		УдалитьРегистрациюИзмененийЭлементаДанных(Ссылка, ПараметрыУдаления);
	КонецЦикла;
	
	ЗавершитьУдалениеРегистрацииПорцииДанных(ПараметрыУдаления);
	
КонецПроцедуры

// Удалить регистрацию изменений подчиненного регистра по указанным регистраторам.
//
// Параметры:
//  Узел - ПланОбменаСсылка.ОбновлениеИнформационнойБазы.
//  Регистраторы - ЛюбаяСсылка
//               - Массив из ЛюбаяСсылка.
//  ПолноеИмяОбъекта - Строка.
//
Процедура УдалитьРегистрациюИзмененийПодчиненногоРегистра(Узел, Регистраторы, ПолноеИмяОбъекта)
	
	ПараметрыУдаления = НовыеПараметрыУдаленияРегистрации(Узел, ПолноеИмяОбъекта);
	
	Для Каждого Регистратор Из МассивЭлементов(Регистраторы) Цикл
		Набор = ПолучитьПереиспользуемыйНабор(ПараметрыУдаления.ПереиспользуемыеНаборы);
		Набор.Отбор.Регистратор.Установить(Регистратор);
		
		ЗаписатьПрогрессВыполненияОбработчика(Набор, Узел); // @skip-check query-in-loop - нет запроса в цикле.
		УдалитьРегистрациюИзмененийЭлементаДанных(Набор, ПараметрыУдаления);
	КонецЦикла;
	
	ЗавершитьУдалениеРегистрацииПорцииДанных(ПараметрыУдаления);
	
КонецПроцедуры

// Удалить регистрацию изменений независимого регистра по таблице значений.
//
// Параметры:
//  Узел - ПланОбменаСсылка.ОбновлениеИнформационнойБазы.
//  Записи - ТаблицаЗначений.
//  ПолноеИмяОбъекта - Строка.
//
Процедура УдалитьРегистрациюИзмененийНезависимогоРегистра(Узел, Записи, ПолноеИмяОбъекта)
	
	ПараметрыУдаления = НовыеПараметрыУдаленияРегистрации(Узел, ПолноеИмяОбъекта);
	МетаданныеРегистра = ОбщегоНазначения.ОбъектМетаданныхПоПолномуИмени(ПолноеИмяОбъекта);
	
	Для Каждого Запись Из Записи Цикл
		Набор = ПолучитьПереиспользуемыйНабор(ПараметрыУдаления.ПереиспользуемыеНаборы);
		УстановитьНедостающиеОтборыВНаборе(Набор, МетаданныеРегистра, Записи);
		
		Для Каждого Колонка Из Записи.Колонки Цикл
			Набор.Отбор[Колонка.Имя].Значение = Запись[Колонка.Имя];
			Набор.Отбор[Колонка.Имя].Использование = Истина;
		КонецЦикла;
		
		ЗаписатьПрогрессВыполненияОбработчика(Набор, Узел, МетаданныеРегистра); // @skip-check query-in-loop - нет запроса в цикле.
		УдалитьРегистрациюИзмененийЭлементаДанных(Набор, ПараметрыУдаления);
	КонецЦикла;
	
	ЗавершитьУдалениеРегистрацииПорцииДанных(ПараметрыУдаления);
	
КонецПроцедуры

// Удалить регистрацию пакета данных, если он уже достаточно большой
// или это нужно сделать принудительно в конце удаления регистрации порции данных.
//
// Параметры:
//  ПараметрыУдаления - см. НовыеПараметрыУдаленияРегистрации.
//  Данные - Массив из ЛюбаяСсылка
//         - Массив из РегистрСведенийНаборЗаписей
//         - Массив из РегистрНакопленияНаборЗаписей
//         - Массив из РегистрБухгалтерииНаборЗаписей
//         - Массив из РегистрРасчетаНаборЗаписей.
//  Принудительно - Булево - Истина, если нужно удалить, даже если пакет мал.
//  РазмерПакета - Число - если в пакете столько же или больше записей, то он удаляется.
//
Процедура УдалитьРегистрациюИзмененийПакетаДанных(ПараметрыУдаления, Данные, Принудительно = Ложь, РазмерПакета = 1000)
	
	Если Данные.Количество() >= РазмерПакета Или Принудительно И Данные.Количество() > 0 Тогда
		ПланыОбмена.УдалитьРегистрациюИзменений(ПараметрыУдаления.Узел, Данные);
		Данные.Очистить();
		
		ОсвободитьПереиспользуемыеНаборыВПараметрах(ПараметрыУдаления);
	КонецЕсли;
	
КонецПроцедуры

// Удалить регистрацию элемента данных в таблице регистрации изменений.
//
// Параметры:
//  Данные - ЛюбаяСсылка
//         - РегистрСведенийНаборЗаписей
//         - РегистрНакопленияНаборЗаписей
//         - РегистрБухгалтерииНаборЗаписей
//         - РегистрРасчетаНаборЗаписей.
//  ПараметрыУдаления - см. НовыеПараметрыУдаленияРегистрации.
//
Процедура УдалитьРегистрациюИзмененийЭлементаДанных(Данные, ПараметрыУдаления)
	
	Если ПараметрыУдаления.ДоступнаПакетнаяРегистрация Тогда
		ПараметрыУдаления.ДанныеДляУдаления.Добавить(Данные);
		УдалитьРегистрациюИзмененийПакетаДанных(ПараметрыУдаления, ПараметрыУдаления.ДанныеДляУдаления);
	Иначе
		ПланыОбмена.УдалитьРегистрациюИзменений(ПараметрыУдаления.Узел, Данные);
		ОсвободитьПереиспользуемыеНаборыВПараметрах(ПараметрыУдаления);
	КонецЕсли;
	
КонецПроцедуры

// Удалить оставшуюся порцию данных в конце процесса удаления регистрации, если доступна пакетная регистрация.
//
// Параметры:
//  ПараметрыУдаления - см. НовыеПараметрыУдаленияРегистрации.
//
Процедура ЗавершитьУдалениеРегистрацииПорцииДанных(ПараметрыУдаления)
	
	Если ПараметрыУдаления.ДоступнаПакетнаяРегистрация Тогда
		УдалитьРегистрациюИзмененийПакетаДанных(ПараметрыУдаления, ПараметрыУдаления.ДанныеДляУдаления, Истина);
	КонецЕсли;
	
КонецПроцедуры

// Необходимо ли записывать данные для обновления в файл при использовании РИБ с фильтрами.
//
// Возвращаемое значение:
//  Булево - Истина - необходимо записывать данные в файл для РИБ.
//
Функция ЗаписыватьДанныеДляОбновленияВФайл(Параметры)
	
	Возврат ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.ОбменДанными")
	      И СтандартныеПодсистемыПовтИсп.ИспользуетсяРИБ("СФильтром")
	   И Не Параметры.ПовторнаяРегистрация
	   И Не ОбщегоНазначения.ЭтоПодчиненныйУзелРИБ();
	
КонецФункции

// Получить общий модуль ОбменДаннымиСервер, если он доступен.
//
// Возвращаемое значение:
//  ОбщийМодуль.
//
Функция МодульОбменДаннымиСервер(ЗаписыватьДанныеДляОбновленияВФайл)
	
	Возврат ?(ЗаписыватьДанныеДляОбновленияВФайл, ОбщегоНазначения.ОбщийМодуль("ОбменДаннымиСервер"), Неопределено);
	
КонецФункции

// Вернуть массив элементов, даже если передан один элемент.
//
// Параметры:
//  Элементы - Массив
//           - Произвольный.
//
// Возвращаемое значение:
//  Массив.
//
Функция МассивЭлементов(Элементы)
	
	Если ТипЗнч(Элементы) <> Тип("Массив") Тогда
		МассивЭлементов = Новый Массив;
		МассивЭлементов.Добавить(Элементы);
	Иначе
		МассивЭлементов = Элементы;
	КонецЕсли;
	
	Возврат МассивЭлементов;
	
КонецФункции

// Структура с контекстом регистрации данных на плане обмена.
//
// Параметры:
//  Узел - ПланОбменаСсылка.ОбновлениеИнформационнойБазы.
//  ВидДанных - Строка.
//  ПолноеИмяОбъекта - Строка.
//
// Возвращаемое значение:
//  Структура:
//   * Параметры - см. ОбновлениеИнформационнойБазы.ОсновныеПараметрыОтметкиКОбработке.
//   * Узел - ПланОбменаСсылка.ОбновлениеИнформационнойБазы.
//   * ВидДанных - Строка.
//   * ПолноеИмяОбъекта - Строка.
//   * ДанныеДляРегистрации - Массив.
//   * МенеджерОбъекта - ДокументМенеджер
//                     - СправочникМенеджер
//                     - ПланВидовХарактеристикМенеджер
//                     - ПланСчетовМенеджер
//                     - ПланыВидовРасчетаМенеджер
//                     - БизнесПроцессМенеджер
//                     - ЗадачаМенеджер
//                     - РегистрСведенийМенеджер
//                     - РегистрНакопленияМенеджер
//                     - РегистрБухгалтерииМенеджер
//                     - РегистрРасчетаМенеджер.
//   * ДоступнаПакетнаяРегистрация - Булево.
//   * ЗаписыватьДанныеДляОбновленияВФайл - Булево.
//   * МодульОбменДаннымиСервер - ОбщийМодуль.
//
Функция НовыеПараметрыРегистрации(Параметры, Узел, ВидДанных, ПолноеИмяОбъекта)
	
	ПараметрыРегистрации = Новый Структура;
	ПараметрыРегистрации.Вставить("Параметры", Параметры);
	ПараметрыРегистрации.Вставить("Узел", Узел);
	ПараметрыРегистрации.Вставить("ВидДанных", ВидДанных);
	ПараметрыРегистрации.Вставить("ПолноеИмяОбъекта", ПолноеИмяОбъекта);
	ПараметрыРегистрации.Вставить("ДанныеДляРегистрации", Новый Массив);
	ПараметрыРегистрации.Вставить("ДоступнаПакетнаяРегистрация", Истина);
	ПараметрыРегистрации.Вставить("ЗаписыватьДанныеДляОбновленияВФайл", ЗаписыватьДанныеДляОбновленияВФайл(Параметры));
	ПараметрыРегистрации.Вставить("МодульОбменДаннымиСервер",
		МодульОбменДаннымиСервер(ПараметрыРегистрации.ЗаписыватьДанныеДляОбновленияВФайл));
	
	МенеджерОбъекта = Неопределено;
	
	Если ЗначениеЗаполнено(ПолноеИмяОбъекта) Тогда
		МенеджерОбъекта = ОбщегоНазначения.МенеджерОбъектаПоПолномуИмени(ПолноеИмяОбъекта);
		
		Если ОбщегоНазначения.ЭтоРегистр(ОбщегоНазначения.ОбъектМетаданныхПоПолномуИмени(ПолноеИмяОбъекта)) Тогда
			ПараметрыРегистрации.Вставить("ПереиспользуемыеНаборы", НовыеПереиспользуемыеНаборы(МенеджерОбъекта));
		КонецЕсли;
	КонецЕсли;
	
	ПараметрыРегистрации.Вставить("МенеджерОбъекта", МенеджерОбъекта);
	
	Возврат ПараметрыРегистрации;
	
КонецФункции

// Структура с контекстом удаления регистрации данных с плана обмена.
//
// Параметры:
//  Узел - ПланОбменаСсылка.ОбновлениеИнформационнойБазы.
//  ПолноеИмяОбъекта - Строка.
//
// Возвращаемое значение:
//  Структура:
//   * Узел - ПланОбменаСсылка.ОбновлениеИнформационнойБазы.
//   * ПолноеИмяОбъекта - Строка.
//   * ДанныеДляУдаления - Массив.
//   * МенеджерОбъекта - ДокументМенеджер
//                     - СправочникМенеджер
//                     - ПланВидовХарактеристикМенеджер
//                     - ПланСчетовМенеджер
//                     - ПланыВидовРасчетаМенеджер
//                     - БизнесПроцессМенеджер
//                     - ЗадачаМенеджер
//                     - РегистрСведенийМенеджер
//                     - РегистрНакопленияМенеджер
//                     - РегистрБухгалтерииМенеджер
//                     - РегистрРасчетаМенеджер.
//   * ДоступнаПакетнаяРегистрация - Булево.
//
Функция НовыеПараметрыУдаленияРегистрации(Узел, ПолноеИмяОбъекта = "")
	
	ПараметрыУдаления = Новый Структура;
	ПараметрыУдаления.Вставить("Узел", Узел);
	ПараметрыУдаления.Вставить("ПолноеИмяОбъекта", ПолноеИмяОбъекта);
	ПараметрыУдаления.Вставить("ДанныеДляУдаления", Новый Массив);
	ПараметрыУдаления.Вставить("ДоступнаПакетнаяРегистрация", Истина);
	
	МенеджерОбъекта = Неопределено;
	
	Если ЗначениеЗаполнено(ПолноеИмяОбъекта) Тогда
		МенеджерОбъекта = ОбщегоНазначения.МенеджерОбъектаПоПолномуИмени(ПолноеИмяОбъекта);
		
		Если ОбщегоНазначения.ЭтоРегистр(ОбщегоНазначения.ОбъектМетаданныхПоПолномуИмени(ПолноеИмяОбъекта)) Тогда
			ПараметрыУдаления.Вставить("ПереиспользуемыеНаборы", НовыеПереиспользуемыеНаборы(МенеджерОбъекта));
		КонецЕсли;
	КонецЕсли;
	
	ПараметрыУдаления.Вставить("МенеджерОбъекта", МенеджерОбъекта);
	
	Возврат ПараметрыУдаления;
	
КонецФункции

// Новая коллекция переиспользуемых наборов.
//
// Параметры:
//  Менеджер - РегистрСведенийМенеджер
//           - РегистрНакопленияМенеджер
//           - РегистрРасчетаМенеджер
//           - РегистрБухгалтерииМенеджер - менеджер для создания наборов.
//  Размер - Число
//
// Возвращаемое значение:
//  Структура:
//   * Наборы - Массив - переиспользуемые наборы.
//   * Создано - Число - количество созданных наборов.
//   * Выдано - Число - количество выданных наборов.
//
Функция НовыеПереиспользуемыеНаборы(Менеджер, Размер = 1000)
	
	ПереиспользуемыеОбъекты = Новый Структура;
	ПереиспользуемыеОбъекты.Вставить("Менеджер", Менеджер);
	ПереиспользуемыеОбъекты.Вставить("Наборы", Новый Массив(Размер));
	ПереиспользуемыеОбъекты.Вставить("Создано", 0);
	ПереиспользуемыеОбъекты.Вставить("Выдано", 0);
	
	Возврат ПереиспользуемыеОбъекты;
	
КонецФункции

// Получить ранее созданный переиспользуемый набор или создать новый и получить его.
//
// Параметры:
//  ПереиспользуемыеНаборы - см. НовыеПереиспользуемыеНаборы.
//
// Возвращаемое значение:
//  - РегистрСведенийНаборЗаписей
//  - РегистрНакопленияНаборЗаписей
//  - РегистрРасчетаНаборЗаписей
//  - РегистрБухгалтерииНаборЗаписей - готовый к работе набор.
//
Функция ПолучитьПереиспользуемыйНабор(ПереиспользуемыеНаборы)
	
	Если ПереиспользуемыеНаборы.Создано = ПереиспользуемыеНаборы.Выдано Тогда
		НовыйНабор = ПереиспользуемыеНаборы.Менеджер.СоздатьНаборЗаписей();
		
		Если ПереиспользуемыеНаборы.Создано > ПереиспользуемыеНаборы.Наборы.ВГраница() Тогда
			ПереиспользуемыеНаборы.Наборы.Добавить(НовыйНабор);
		Иначе
			ПереиспользуемыеНаборы.Наборы[ПереиспользуемыеНаборы.Создано] = НовыйНабор;
		КонецЕсли;
		
		ПереиспользуемыеНаборы.Создано = ПереиспользуемыеНаборы.Создано + 1;
	КонецЕсли;
	
	ВозвращаемыйНабор = ПереиспользуемыеНаборы.Наборы[ПереиспользуемыеНаборы.Выдано];
	ПереиспользуемыеНаборы.Выдано = ПереиспользуемыеНаборы.Выдано + 1;
	
	Возврат ВозвращаемыйНабор;
	
КонецФункции

// Освободить ранее занятые переиспользуемые наборы.
//
// Параметры:
//  ПереиспользуемыеНаборы - см. НовыеПереиспользуемыеНаборы.
//
Процедура ОсвободитьПереиспользуемыеНаборы(ПереиспользуемыеНаборы)
	
	ПереиспользуемыеНаборы.Выдано = 0;
	
КонецПроцедуры

// Освободить ранее занятые переиспользуемые наборы на основе параметров.
//
// Параметры:
//  Параметры - см. НовыеПараметрыРегистрации
//            - см. НовыеПараметрыУдаленияРегистрации.
//
Процедура ОсвободитьПереиспользуемыеНаборыВПараметрах(Параметры)
	
	Если Параметры.Свойство("ПереиспользуемыеНаборы") Тогда
		ОсвободитьПереиспользуемыеНаборы(Параметры.ПереиспользуемыеНаборы);
	КонецЕсли;
	
КонецПроцедуры

Функция УзлыМеньшейОчереди(Очередь)
	Возврат ПланыОбмена.ОбновлениеИнформационнойБазы.УзлыМеньшейОчереди(Очередь);
КонецФункции

Функция ОчередьСсылкой(Очередь, Временная = Ложь)
	Возврат ПланыОбмена.ОбновлениеИнформационнойБазы.УзелПоОчереди(Очередь, Временная);
КонецФункции

// Контекст построения выборки при помощи таких функций, как:
// - ВыбратьРегистраторыРегистраДляОбработки();
// - ВыбратьСсылкиДляОбработки();
// - ВыбратьИзмеренияНезависимогоРегистраСведенийДляОбработки();
//
// Параметры:
//  ДополнительныеПараметры - см. ОбновлениеИнформационнойБазы.ДополнительныеПараметрыВыборкиДанныхДляОбработки
//  ИмяТаблицы - Строка - имя таблицы из которой выбираются данные.
//
// Возвращаемое значение:
//  Структура:
//   * ДополнительныеПараметры - Структура - копия ссылки входного параметра для процедур механизма.
//   * ПостраничнаяВыборка - Булево - Истина, если выборка выполняется по страницам.
//   * ИмяТаблицы - Строка - имя таблицы из которой выбираются данные.
//   * ПоляВыборки - Массив - поля, которые подставляются в список выборки запроса.
//   * ПоляУпорядочивания - Массив - поля, которые подставляются в раздел упорядочивания запроса.
//   * ИспользуемыеПоляУпорядочивания - Соответствие - кеш полей, которые уже использованы для упорядочивания.
//   * Псевдонимы - Массив - псевдонимы имен выбираемых полей, подставляемых в запрос выборки.
//   * Направления - Массив - направления упорядочивания (УБЫВ, ВОЗР).
//
Функция ПараметрыПостроенияВыборки(ДополнительныеПараметры, ИмяТаблицы = Неопределено)
	
	ПроверитьПараметрыВыборки(ДополнительныеПараметры);
	
	ПараметрыПостроения = Новый Структура;
	ПараметрыПостроения.Вставить("ДополнительныеПараметры", ДополнительныеПараметры);
	ПараметрыПостроения.Вставить("ПостраничнаяВыборка", ЭтоПостраничнаяВыборка(ДополнительныеПараметры));
	ПараметрыПостроения.Вставить("ИмяТаблицы", ИмяТаблицы);
	ПараметрыПостроения.Вставить("ПоляВыборки", Новый Массив);
	ПараметрыПостроения.Вставить("ПоляУпорядочивания", Новый Массив);
	ПараметрыПостроения.Вставить("ИспользуемыеПоляУпорядочивания", Новый Соответствие);
	ПараметрыПостроения.Вставить("Псевдонимы", Новый Массив);
	ПараметрыПостроения.Вставить("Направления", Новый Массив);
	
	Возврат ПараметрыПостроения;
	
КонецФункции

// Установить поля упорядочивания в ВыбратьСсылкиДляОбработки().
//
// Параметры:
//  ПараметрыПостроения - см. ПараметрыПостроенияВыборки
//  ЭтоДокумент - Булево - Истина, если обрабатываются ссылки документа.
//
Процедура УстановитьПоляУпорядочиванияСсылок(ПараметрыПостроения, ЭтоДокумент)
	
	ПоляВыборки = ПараметрыПостроения.ПоляВыборки;
	ПоляУпорядочивания = ПараметрыПостроения.ПоляУпорядочивания;
	ПостраничнаяВыборка = ПараметрыПостроения.ПостраничнаяВыборка;
	
	Если ЭтоДокумент Тогда
		Если ПостраничнаяВыборка Тогда
			ПоляВыборки.Добавить("ТаблицаОбъекта.Дата");
		КонецЕсли;
		
		ПоляУпорядочивания.Добавить("ТаблицаОбъекта.Дата");
	КонецЕсли;
	
	ПоляВыборки.Добавить("ТаблицаИзменений.Ссылка");
	
	Если ПостраничнаяВыборка Тогда
		ПоляУпорядочивания.Добавить("ТаблицаИзменений.Ссылка");
	КонецЕсли;
	
КонецПроцедуры

// Установить поля упорядочивания для регистра в ВыбратьРегистраторыРегистраДляОбработки().
//
// Параметры:
//  ПараметрыПостроения - см. ПараметрыПостроенияВыборки
//
Процедура УстановитьПоляУпорядочиванияРегистра(ПараметрыПостроения)
	
	ПоляВыборки = ПараметрыПостроения.ПоляВыборки;
	Псевдонимы = ПараметрыПостроения.Псевдонимы;
	ПоляУпорядочивания = ПараметрыПостроения.ПоляУпорядочивания;
	ИмяИзмеренияДляОтбора = ПараметрыПостроения.ДополнительныеПараметры.ИмяИзмеренияДляОтбора;
	ПолеРегистратора = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку("ТаблицаРегистраИзменения.%1",
		ИмяИзмеренияДляОтбора);
	
	ПоляВыборки.Добавить(ПолеРегистратора);
	Псевдонимы.Добавить(ИмяИзмеренияДляОтбора);
	
	Если ПараметрыПостроения.ПостраничнаяВыборка Тогда
		ПоляУпорядочивания.Добавить(ПолеРегистратора);
	Иначе
		Если ВРег(ИмяИзмеренияДляОтбора) = ВРег("Регистратор") Тогда
			ПоляВыборки.Добавить("МАКСИМУМ(ЕСТЬNULL(ТаблицаРегистра.Период, ДАТАВРЕМЯ(3000, 1, 1)))");
			Псевдонимы.Добавить("Период");
			ПоляУпорядочивания.Добавить("МАКСИМУМ(ЕСТЬNULL(ТаблицаРегистра.Период, ДАТАВРЕМЯ(3000, 1, 1)))");
		Иначе
			ПолеДаты = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку("%1.Дата", ПолеРегистратора);
			ПоляВыборки.Добавить(ПолеДаты);
			Псевдонимы.Добавить("Период");
			ПоляУпорядочивания.Добавить(ПолеДаты);
		КонецЕсли;
	КонецЕсли;
	
КонецПроцедуры

// Установить поля упорядочивания для документа в ВыбратьРегистраторыРегистраДляОбработки().
//
// Параметры:
//  ПараметрыПостроения - см. ПараметрыПостроенияВыборки
//
Процедура УстановитьПоляУпорядочиванияРегистраПоДокументу(ПараметрыПостроения)
	
	ПоляВыборки = ПараметрыПостроения.ПоляВыборки;
	Псевдонимы = ПараметрыПостроения.Псевдонимы;
	ПоляУпорядочивания = ПараметрыПостроения.ПоляУпорядочивания;
	ПоляУпорядочивания.Добавить("ТаблицаДокумента.Дата");
	ИмяИзмеренияДляОтбора = ПараметрыПостроения.ДополнительныеПараметры.ИмяИзмеренияДляОтбора;
	ПолеРегистратора = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку("ТаблицаРегистраИзменения.%1",
		ИмяИзмеренияДляОтбора);
	
	Если ПараметрыПостроения.ПостраничнаяВыборка Тогда
		ПоляВыборки.Добавить("ТаблицаДокумента.Дата");
		Псевдонимы.Добавить("Дата");
		ПоляУпорядочивания.Добавить(ПолеРегистратора);
	КонецЕсли;
	
	ПоляВыборки.Добавить(ПолеРегистратора);
	Псевдонимы.Добавить(ИмяИзмеренияДляОтбора);
	
КонецПроцедуры

// Установить поля упорядочивания для документа в ВыбратьИзмеренияНезависимогоРегистраСведенийДляОбработки().
//
// Параметры:
//  ПараметрыПостроения - см. ПараметрыПостроенияВыборки
//
Процедура УстановитьПоляУпорядочиванияНезависимогоРегистраСведений(ПараметрыПостроения)
	
	Разделители = " " + Символы.Таб + Символы.ПС;
	ПоляУпорядочивания = ПараметрыПостроения.ДополнительныеПараметры.ПоляУпорядочивания;
	
	Для ИндексПоля = 0 По ПоляУпорядочивания.ВГраница() Цикл
		Поле = ПоляУпорядочивания[ИндексПоля];
		Состав = СтрРазделить(Поле, Разделители, Ложь);
		ИмяПоля = Состав[0];
		ПараметрыПостроения.ПоляВыборки.Добавить(ИмяПоля);
		ПараметрыПостроения.ИспользуемыеПоляУпорядочивания[ИмяПоля] = Истина;
		
		Если Состав.Количество() > 1 Тогда
			ПараметрыПостроения.Направления.Добавить(Состав[1]);
		Иначе
			ПараметрыПостроения.Направления.Добавить(?(ИндексПоля = 0, "УБЫВ", ""));
		КонецЕсли;
	КонецЦикла;
	
КонецПроцедуры

// Учесть измерение в параметрах формирования запроса в ВыбратьИзмеренияНезависимогоРегистраСведенийДляОбработки().
//
// Параметры:
//  ПараметрыПостроения - см. ПараметрыПостроенияВыборки
//  ИмяИзмерения - Строка - имя обрабатываемого измерения.
//
Процедура УстановитьИзмерение(ПараметрыПостроения, ИмяИзмерения)
	
	Если ПараметрыПостроения.ИспользуемыеПоляУпорядочивания[ИмяИзмерения] = Неопределено Тогда
		ПараметрыПостроения.ПоляВыборки.Добавить(ИмяИзмерения);
		ЗаданыПоляУпорядочивания = ЗаданыПоляУпорядочивания(ПараметрыПостроения.ДополнительныеПараметры);
		
		Если ЗаданыПоляУпорядочивания Или ПараметрыПостроения.ПостраничнаяВыборка Тогда
			ПараметрыПостроения.Направления.Добавить("");
		КонецЕсли;
	КонецЕсли;
	
КонецПроцедуры

// Учесть период в параметрах формирования запроса в ВыбратьИзмеренияНезависимогоРегистраСведенийДляОбработки().
//
// Параметры:
//  ПараметрыПостроения - см. ПараметрыПостроенияВыборки
//
Процедура УстановитьПериод(ПараметрыПостроения)
	
	ПараметрыПостроения.ПоляВыборки.Вставить(0, "Период");
	ПараметрыПостроения.Направления.Вставить(0, "");
	
КонецПроцедуры

// Учесть ресурсы в параметрах формирования запроса в ВыбратьИзмеренияНезависимогоРегистраСведенийДляОбработки().
//
// Параметры:
//  ПараметрыПостроения - см. ПараметрыПостроенияВыборки
//  Ресурсы             - КоллекцияОбъектовМетаданных
//
Процедура УстановитьРесурсы(ПараметрыПостроения, Ресурсы)
	
	Если ПараметрыПостроения.ПоляВыборки.Количество() = 0 Тогда
		Для Каждого Ресурс Из Ресурсы Цикл
			Если ПараметрыПостроения.ИспользуемыеПоляУпорядочивания[Ресурс.Имя] = Неопределено Тогда
				ПараметрыПостроения.ПоляВыборки.Добавить(Ресурс.Имя);
				ПараметрыПостроения.Направления.Добавить("");
			КонецЕсли;
		КонецЦикла;
	КонецЕсли;
	
КонецПроцедуры

// Учесть реквизиты в параметрах формирования запроса в ВыбратьИзмеренияНезависимогоРегистраСведенийДляОбработки().
//
// Параметры:
//  ПараметрыПостроения - см. ПараметрыПостроенияВыборки
//  Реквизиты           - КоллекцияОбъектовМетаданных
//
Процедура УстановитьРеквизиты(ПараметрыПостроения, Реквизиты)
	
	Если ПараметрыПостроения.ПоляВыборки.Количество() = 0 Тогда
		Для Каждого Реквизит Из Реквизиты Цикл
			Если ПараметрыПостроения.ИспользуемыеПоляУпорядочивания[Реквизит.Имя] = Неопределено Тогда
				ПараметрыПостроения.ПоляВыборки.Добавить(Реквизит.Имя);
				ПараметрыПостроения.Направления.Добавить("");
			КонецЕсли;
		КонецЦикла;
	КонецЕсли;
	
КонецПроцедуры

// Выбрать данные в следующих функциях с учетом постраничной выборки:
// - ВыбратьРегистраторыРегистраДляОбработки();
// - ВыбратьСсылкиДляОбработки();
// - ВыбратьИзмеренияНезависимогоРегистраСведенийДляОбработки();
//
// Параметры:
//  Запрос - Запрос - запрос выборки данных.
//  ПараметрыПостроения - см. ПараметрыПостроенияВыборки
//
// Возвращаемое значение:
//   ВыборкаИзРезультатаЗапроса - в случае обычной выборки.
//   ТаблицаЗначений - в случае постраничной выборки.
//
Функция ВыбратьДанныеДляОбработки(Запрос, ПараметрыПостроения)
	
	Если ПараметрыПостроения.ПостраничнаяВыборка Тогда
		Возврат ВыбратьДанныеПостранично(Запрос, ПараметрыПостроения);
	Иначе
		Запрос.Текст = СтрЗаменить(Запрос.Текст, "&УсловиеПоСтраницам", "ИСТИНА");
		Возврат Запрос.Выполнить().Выбрать();
	КонецЕсли;
	
КонецФункции

// Получить таблицу значений с данными для обработки с учетом постраничной выборки.
//
// Параметры:
//  Запрос - Запрос - запрос выборки данных.
//  ПараметрыПостроения - см. ПараметрыПостроенияВыборки
//
// Возвращаемое значение:
//  ТаблицаЗначений - данные для обработчика обновления (многопоточного).
//
Функция ВыбратьДанныеПостранично(Запрос, ПараметрыПостроения)
	
	ПоляВыборки = ПараметрыПостроения.ПоляВыборки;
	Параметры = ПараметрыПостроения.ДополнительныеПараметры;
	ТаблицаИзменений = ПараметрыПостроения.ИмяТаблицы;
	Направления = ПараметрыПостроения.Направления;
	ПоследняяВыбраннаяЗапись = Параметры.ПоследняяВыбраннаяЗапись;
	ПерваяЗапись = Параметры.ПерваяЗапись;
	ПоследняяЗапись = Параметры.ПоследняяЗапись;
	ВыбратьПервые = ПоследняяВыбраннаяЗапись = Неопределено
	              И ПерваяЗапись = Неопределено
	              И ПоследняяЗапись = Неопределено;
	
	Если ВыбратьПервые Тогда
		ИзменитьМаксимумВыборки(Запрос.Текст, МаксимальноеКоличествоЗаписейВВыборке(), Параметры.МаксимумВыборки);
		Запрос.Текст = СтрЗаменить(Запрос.Текст, "&УсловиеПоСтраницам", "ИСТИНА");
		Результат = Запрос.Выполнить().Выгрузить();
	Иначе
		ВыбратьДиапазон = ПерваяЗапись <> Неопределено И ПоследняяЗапись <> Неопределено;
		БазовыйТекстЗапроса = Запрос.Текст;
		
		Если ВыбратьДиапазон Тогда
			Условия = УсловияДляДиапазонаСтраницы(ПоляВыборки, Параметры, Направления);
		Иначе
			Условия = УсловияДляСледующейСтраницы(ПоляВыборки, Параметры, Направления);
		КонецЕсли;
		
		Если Параметры.ОптимизироватьВыборкуПоСтраницам Тогда
			Результат = Неопределено;
			ИндексПоследнегоУсловия = Условия.Количество() - 1;
			ОтложитьУничтожениеВременныхТаблиц = Условия.Количество() > 1;
			
			Если ОтложитьУничтожениеВременныхТаблиц Тогда
				ТекстЗапросаУничтоженияВременныхТаблиц = ВырезатьУничтожениеВременныхТаблиц(БазовыйТекстЗапроса);
			КонецЕсли;
			
			Для Индекс = 0 По ИндексПоследнегоУсловия Цикл
				Если Результат = Неопределено Тогда
					Количество = Параметры.МаксимумВыборки;
				Иначе
					Количество = Параметры.МаксимумВыборки - Результат.Количество();
				КонецЕсли;
				
				Запрос.Текст = Строка(БазовыйТекстЗапроса);
				ИзменитьМаксимумВыборки(Запрос.Текст, МаксимальноеКоличествоЗаписейВВыборке(), Количество);
				УстановитьУсловияВыборкиПоСтраницам(Запрос, Условия, ТаблицаИзменений, Параметры, Истина, Индекс);
				
				Если ОтложитьУничтожениеВременныхТаблиц И Индекс = ИндексПоследнегоУсловия Тогда
					Запрос.Текст = Запрос.Текст + ТекстЗапросаУничтоженияВременныхТаблиц;
				КонецЕсли;
				
				Выгрузка = Запрос.Выполнить().Выгрузить(); // @skip-check query-in-loop - оптимизированная выборка данных.
				
				Если Результат = Неопределено Тогда
					Результат = Выгрузка;
				Иначе
					Для Каждого СтрокаВыгрузки Из Выгрузка Цикл
						СтрокаРезультата = Результат.Добавить();
						ЗаполнитьЗначенияСвойств(СтрокаРезультата, СтрокаВыгрузки);
					КонецЦикла;
				КонецЕсли;
				
				Если Результат.Количество() = Параметры.МаксимумВыборки Тогда
					Прервать;
				КонецЕсли;
			КонецЦикла;
		Иначе
			ИзменитьМаксимумВыборки(Запрос.Текст, МаксимальноеКоличествоЗаписейВВыборке(), Параметры.МаксимумВыборки);
			УстановитьУсловияВыборкиПоСтраницам(Запрос, Условия, ТаблицаИзменений, Параметры, Истина);
			Результат = Запрос.Выполнить().Выгрузить();
		КонецЕсли;
	КонецЕсли;
	
	Возврат Результат;
	
КонецФункции

// Удалить из пакета запросов запросы удаления временных таблиц и вернуть их, как результат этой функции.
//
// Параметры:
//  ТекстЗапроса - Строка - изменяемый текст запроса.
//
// Возвращаемое значение:
//  Строка - фрагмент текста запроса с удалением временных таблиц.
//
Функция ВырезатьУничтожениеВременныхТаблиц(ТекстЗапроса)
	
	ЗапросыУничтожения = Новый Массив;
	ПозицияУничтожить = СтрНайти(ТекстЗапроса, "УНИЧТОЖИТЬ");
	
	Пока ПозицияУничтожить > 0 Цикл
		ПозицияРазделителя = СтрНайти(ТекстЗапроса, ";",, ПозицияУничтожить);
		
		Если ПозицияРазделителя > 0 Тогда
			ЗапросУничтожения = Сред(ТекстЗапроса, ПозицияУничтожить, ПозицияРазделителя - ПозицияУничтожить + 1);
		Иначе
			ЗапросУничтожения = Сред(ТекстЗапроса, ПозицияУничтожить);
		КонецЕсли;
		
		Если ЗапросыУничтожения.Количество() = 0 Тогда
			ЗапросыУничтожения.Добавить(Символы.ПС);
		КонецЕсли;
		
		ЗапросыУничтожения.Добавить(ЗапросУничтожения);
		ТекстЗапроса = СтрЗаменить(ТекстЗапроса, ЗапросУничтожения, "");
		ПозицияУничтожить = СтрНайти(ТекстЗапроса, "УНИЧТОЖИТЬ");
	КонецЦикла;
	
	Возврат СтрСоединить(ЗапросыУничтожения, Символы.ПС);
	
КонецФункции

// Добавить в запрос условия, ограничивающие выборку по страницам.
//
// Постраничная выборка работает в двух режимах:
// - выборка сверху - записи большие указанной (аналогично динамическому списку);
// - выборка диапазона - записи между двумя указанными записями включительно.
//
// Параметры:
//  Запрос  - Запрос - запрос выборки данных.
//  Условия - см. НовыеУсловияПостраничнойВыборки
//  Таблица - Строка - имя таблицы из которой выполняется выборка.
//  Параметры - см. ОбновлениеИнформационнойБазы.ДополнительныеПараметрыОтметкиОбработки
//  Первые - Булево - Истина, если это первые условия в запросе.
//  НомерУсловия - Число - номер обрабатываемого условия.
//
Процедура УстановитьУсловияВыборкиПоСтраницам(Запрос, Условия, Таблица, Параметры, Первые, НомерУсловия = Неопределено)
	
	ПерваяЗапись = Параметры.ПерваяЗапись;
	ПоследняяЗапись = Параметры.ПоследняяЗапись;
	ВыбратьДиапазон = ПерваяЗапись <> Неопределено
	                И ПоследняяЗапись <> Неопределено;
	
	Если Не ВыбратьДиапазон Тогда
		ПерваяЗапись = Параметры.ПоследняяВыбраннаяЗапись;
	КонецЕсли;
	
	Колонки = Условия.Колонки;
	КоличествоКолонок = Колонки.Количество();
	УсловияИ = Новый Массив;
	УсловияИли = Новый Массив;
	ЕстьУсловияИли = (НомерУсловия = Неопределено);
	ЕстьТаблица = Не ПустаяСтрока(Таблица);
	ШаблонУсловияИ = ?(ЕстьТаблица, Таблица + ".%1 %2 &%3", "%1 %2 &%3");
	ШаблонУсловияИли = "(%1)";
	РазделительУсловийИ =
		"
		|	И ";
	РазделительУсловийИли =
		"
		|	) ИЛИ (
		|	";
	
	Если ЕстьУсловияИли Тогда
		ИндексНачала = 0;
		ИндексОкончания = Условия.Количество() - 1;
	Иначе
		ИндексНачала = НомерУсловия;
		ИндексОкончания = НомерУсловия;
	КонецЕсли;
	
	Для ИндексСтроки = ИндексНачала По ИндексОкончания Цикл
		УсловияИ.Очистить();
		
		Для ИндексКолонки = 0 По КоличествоКолонок - 1 Цикл
			Оператор = Условия[ИндексСтроки][ИндексКолонки];
			ИндексПоля = ?(ВыбратьДиапазон, Цел(ИндексКолонки / 2), ИндексКолонки) + 2;
			
			Если Не ПустаяСтрока(Оператор) Тогда
				Колонка = Колонки[ИндексКолонки];
				ИмяПоляПолное = Колонка.Заголовок;
				ИмяПараметра = Колонка.Имя + "Значение";
				ИмяПоля = ИмяКолонкиДляЗапроса(ИмяПоляПолное);
				Условие = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(ШаблонУсловияИ, ИмяПоля, Оператор, ИмяПараметра);
				УсловияИ.Добавить(Условие);
				
				Если ЭтоИмяКолонкиКонцаДиапазона(ИмяПоляПолное) Тогда
					ЗначениеПараметра = ПоследняяЗапись[ИндексПоля].Значение;
				Иначе
					ЗначениеПараметра = ПерваяЗапись[ИндексПоля].Значение;
				КонецЕсли;
				
				Запрос.УстановитьПараметр(ИмяПараметра, ЗначениеПараметра);
			КонецЕсли;
		КонецЦикла;
		
		ТекстУсловий = СтрСоединить(УсловияИ, РазделительУсловийИ);
		
		Если ЕстьУсловияИли Тогда
			УсловияИли.Добавить(ТекстУсловий);
		КонецЕсли;
	КонецЦикла;
	
	Если ЕстьУсловияИли Тогда
		ТекстУсловийИли = СтрСоединить(УсловияИли, РазделительУсловийИли);
		ТекстУсловий = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(ШаблонУсловияИли, ТекстУсловийИли);
	КонецЕсли;
	
	Если Не Первые Тогда
		ТекстУсловий = "	И " + ТекстУсловий;
	КонецЕсли;
	
	Запрос.Текст = СтрЗаменить(Запрос.Текст, "&УсловиеПоСтраницам", ТекстУсловий);
	
КонецПроцедуры

// Получить условия для выборки записей большие указанной (аналогично динамическому списку).
//
// Параметры:
//  ПоляВыборки - Массив - выбираемые запросом поля.
//  Параметры - см. ОбновлениеИнформационнойБазы.ДополнительныеПараметрыОтметкиОбработки.
//  Направления - Массив - направления упорядочивания (ВОЗР, УБЫВ) в количестве равном количеству ПоляВыборки.
//              - Неопределено - порядок не указывается (всегда ВОЗР).
//
// Возвращаемое значение:
//   см. НовыеУсловияПостраничнойВыборки
//
Функция УсловияДляСледующейСтраницы(ПоляВыборки, Параметры, Направления)
	
	ВсеУсловия = НовыеУсловияПостраничнойВыборки(ПоляВыборки);
	КоличествоПолей = ПоляВыборки.Количество();
	
	Пока КоличествоПолей > 0 Цикл
		НовыеУсловия = ВсеУсловия.Добавить();
		
		Для НомерУсловия = 1 По КоличествоПолей Цикл
			ИмяКолонкиПоля = ПоляВыборки[НомерУсловия - 1];
			
			Если НомерУсловия < КоличествоПолей Тогда
				Оператор = "=";
			Иначе
				Оператор = ОператорБольше(Направления[НомерУсловия - 1]);
			КонецЕсли;
			
			НовыеУсловия[ИмяКолонкиИзПоляВыборки(ИмяКолонкиПоля)] = Оператор;
		КонецЦикла;
		
		КоличествоПолей = КоличествоПолей - 1;
	КонецЦикла;
	
	Возврат ВсеУсловия;
	
КонецФункции

// Получить условия для выборки записей между двумя указанными записями включительно.
//
// Параметры:
//  ПоляВыборки - Массив - выбираемые запросом поля.
//  Параметры - см. ОбновлениеИнформационнойБазы.ДополнительныеПараметрыОтметкиОбработки.
//  Направления - Массив - направления упорядочивания (ВОЗР, УБЫВ) в количестве равном количеству ПоляВыборки.
//              - Неопределено - порядок не указывается (всегда ВОЗР).
//
// Возвращаемое значение:
//   см. НовыеУсловияПостраничнойВыборки
//
Функция УсловияДляДиапазонаСтраницы(ПоляВыборки, Параметры, Направления)
	
	ВсеУсловия = НовыеУсловияПостраничнойВыборки(ПоляВыборки, Истина);
	ПерваяЗапись = Параметры.ПерваяЗапись;
	ПоследняяЗапись = Параметры.ПоследняяЗапись;
	КоличествоПолей = ПоляВыборки.Количество();
	ВсегоПолей = ПоляВыборки.Количество();
	ПозицияВставки = 0;
	
	Пока КоличествоПолей > 0 Цикл
		ТекущиеПоляРавны = ЗаписиРавны(ПерваяЗапись, ПоследняяЗапись, КоличествоПолей);
		
		Если ТекущиеПоляРавны И КоличествоПолей <> ВсегоПолей Тогда
			Прервать;
		КонецЕсли;
		
		ПервыеУсловия = ВсеУсловия.Вставить(ПозицияВставки);
		ПозицияВставки = ПозицияВставки + 1;
		ПредыдущиеПоляРавны = ЗаписиРавны(ПерваяЗапись, ПоследняяЗапись, КоличествоПолей - 1);
		
		Если Не ПредыдущиеПоляРавны Тогда
			ПоследниеУсловия = ВсеУсловия.Вставить(ПозицияВставки);
		КонецЕсли;
		
		Для НомерУсловия = 1 По КоличествоПолей Цикл
			ИмяКолонкиПоля = ИмяКолонкиИзПоляВыборки(ПоляВыборки[НомерУсловия - 1]);
			ИмяКолонкиПоляПоДиапазону = ИмяКолонкиДиапазона(ИмяКолонкиПоля);
			
			Если НомерУсловия < КоличествоПолей Или ТекущиеПоляРавны И КоличествоПолей = ВсегоПолей Тогда
				ОператорПервый = "=";
				ОператорПоследний = "=";
			Иначе
				Направление = Направления[НомерУсловия - 1];
				
				Если КоличествоПолей = ВсегоПолей Тогда
					ОператорПервый = ОператорБольшеИлиРавно(Направление);
					ОператорПоследний = ОператорМеньшеИлиРавно(Направление);
				Иначе
					ОператорПервый = ОператорБольше(Направление);
					ОператорПоследний = ОператорМеньше(Направление);
				КонецЕсли;
				
				// Ограничение по диапазону
				Если ПредыдущиеПоляРавны Тогда
					ПервыеУсловия[ИмяКолонкиПоляПоДиапазону] = ОператорПоследний;
				КонецЕсли;
			КонецЕсли;
			
			// Выборка по первой записи
			ПервыеУсловия[ИмяКолонкиПоля] = ОператорПервый;
			
			// Выборка по последней записи
			Если Не ПредыдущиеПоляРавны Тогда
				ПоследниеУсловия[ИмяКолонкиПоляПоДиапазону] = ОператорПоследний;
			КонецЕсли;
		КонецЦикла;
		
		КоличествоПолей = КоличествоПолей - 1;
	КонецЦикла;
	
	Возврат ВсеУсловия;
	
КонецФункции

// Возвращает результат сравнения двух записей.
//
// Параметры:
//  ПерваяЗапись - СписокЗначений из Строка
//  ПоследняяЗапись - СписокЗначений из Строка
//  КоличествоПолей - Число - количество прикладных полей в ключе записи.
//
// Возвращаемое значение:
//  Булево - Истина, если записи равны.
//
Функция ЗаписиРавны(ПерваяЗапись, ПоследняяЗапись, КоличествоПолей)
	
	Для Индекс = 2 По КоличествоПолей + 2 - 1 Цикл
		Если ПерваяЗапись[Индекс].Значение <> ПоследняяЗапись[Индекс].Значение Тогда
			Возврат Ложь;
		КонецЕсли;
	КонецЦикла;
	
	Возврат Истина;
	
КонецФункции

// Возвращает таблицу значений с условиями постраничной выборки.
//
// Параметры:
//  ПоляВыборки - Массив - выбираемые запросом поля.
//  ДляДиапазона - Булево - Истина, если таблица будет использоваться для ограничения по диапазону.
//                 В этом случае, добавляются дополнительные колонки для нижнего условия диапазона.
//
// Возвращаемое значение:
//  ТаблицаЗначений - колонки таблицы описывают поля запроса, где в заголовке хранится полное имя поля.
//                    Имена колонок формируются динамические из имен полей выборки запроса.
//                    Каждая строка описывает условия для одного запроса выбирающего порции данных для страницы
//                    или группы условий при выборке через ИЛИ. В ячейках таблицы хранятся условия
//                    ("=", ">", "<", ">=", "<=")..
// 
Функция НовыеУсловияПостраничнойВыборки(ПоляВыборки, ДляДиапазона = Ложь)
	
	Условия = Новый ТаблицаЗначений;
	Колонки = Условия.Колонки;
	
	Для Каждого ПолеВыборки Из ПоляВыборки Цикл
		Имя = ИмяКолонкиИзПоляВыборки(ПолеВыборки);
		Колонки.Добавить(Имя,, ПолеВыборки);
		
		Если ДляДиапазона Тогда
			Колонки.Добавить(ИмяКолонкиДиапазона(Имя),, ПолеВыборки);
		КонецЕсли;
	КонецЦикла;
	
	Возврат Условия;
	
КонецФункции

// Возвращает имя колонки для условия по нижней границе диапазона постраничной выборки.
//
// Параметры:
//  ИмяПоля - Строка - имя поля.
//
// Возвращаемое значение:
//  Строка - имя поля для условия по нижней границе диапазона.
//
Функция ИмяКолонкиДиапазона(ИмяПоля)
	
	Возврат ИмяПоля + ПостфиксКонцаДиапазона();
	
КонецФункции

// Возвращает имя, например, для колонки таблицы значений, полученное из полного имени поля запроса.
//
// Параметры:
//  Имя - Строка - полное имя поля запроса (возможно, разделенное точкой).
//
// Возвращаемое значение:
//  Строка - имя поля.
//
Функция ИмяКолонкиИзПоляВыборки(Имя)
	
	ЗаменяемыеСимволы = ".,() ";
	ИмяКолонки = Строка(Имя);
	
	Для Индекс = 1 По СтрДлина(ЗаменяемыеСимволы) Цикл
		Символ = Сред(ЗаменяемыеСимволы, Индекс, 1);
		ИмяКолонки = СтрЗаменить(ИмяКолонки, Символ, "_");
	КонецЦикла;
	
	Возврат ИмяКолонки;
	
КонецФункции

// Имя поля, полученное из колонки таблицы условий постраничной выборки, см. НовыеУсловияПостраничнойВыборки.
//
// Параметры:
//  ИмяПоля - Строка - имя поля.
//
// Возвращаемое значение:
//  Строка - имя поля без служебных символов.
//
Функция ИмяКолонкиДляЗапроса(ИмяПоля)
	
	Если ЭтоИмяКолонкиКонцаДиапазона(ИмяПоля) Тогда
		Возврат Лев(ИмяПоля, СтрДлина(ИмяПоля) - СтрДлина(ПостфиксКонцаДиапазона()));
	Иначе
		Возврат ИмяПоля;
	КонецЕсли;
	
КонецФункции

// Определяет, описывает ли колонка конец диапазона постраничной выборки.
//
// Параметры:
//  ИмяПоля - Строка - имя поля.
//
// Возвращаемое значение:
//  Булево - Истина, если это поле описывает конец диапазона постраничной выборки.
//
Функция ЭтоИмяКолонкиКонцаДиапазона(ИмяПоля)
	
	Возврат СтрЗаканчиваетсяНа(ИмяПоля, ПостфиксКонцаДиапазона());
	
КонецФункции

Функция ПостфиксКонцаДиапазона()
	
	Возврат "_" + "КонецДиапазона";
	
КонецФункции

// Проверка корректности заполнения параметров выборки данных.
//
// Параметры:
//  Параметры - см. ОбновлениеИнформационнойБазы.ДополнительныеПараметрыВыборкиДанныхДляОбработки.
//
Процедура ПроверитьПараметрыВыборки(Параметры)
	
	Если Не Параметры.ВыбиратьПорциями И ЭтоПостраничнаяВыборка(Параметры) Тогда
		ВызватьИсключение НСтр("ru = 'Многопоточный обработчик обновления обязан выбирать данные порциями.'");
	КонецЕсли;
	
КонецПроцедуры

// Формирует фрагмент текста запроса с выбираемыми полями.
//
// Параметры:
//  ИменаПолей - Массив - имена полей в виде массива.
//  Псевдонимы - Массив - псевдонимы в виде массива с тем же количеством элементов, что и в ИменаПолей.
//             - Неопределено - в этом случае, псевдонимы равны именам полей.
//  ИмяТаблицы - Строка - имя таблицы в которой расположены поля.
//                        если указана пустая строка, то имя таблицы не подставляется.
//  Дополнительные - Булево - Истина, если это не первые поля в выборке и перед ними нужна ",".
//
// Возвращаемое значение:
//  Строка - фрагмент текста запроса с выбираемыми полями.
//
Функция ПоляДляЗапроса(ИменаПолей, Псевдонимы = Неопределено, ИмяТаблицы = "", Дополнительные = Ложь)
	
	КоличествоПолей = ИменаПолей.Количество();
	
	Если КоличествоПолей = 0 Тогда
		Возврат "";
	КонецЕсли;
	
	ЕстьПсевдонимы = Псевдонимы <> Неопределено И Псевдонимы.Количество() = КоличествоПолей;
	ПолноеИмяТаблицы = ?(ПустаяСтрока(ИмяТаблицы), "", ИмяТаблицы + ".");
	ИспользуемыеПсевдонимы = Новый Соответствие;
	Поля = Новый Массив;
	
	Для Индекс = 0 По КоличествоПолей - 1 Цикл
		ИмяПоля = ИменаПолей[Индекс];
		
		Если ЕстьПсевдонимы Тогда
			Псевдоним = Псевдонимы[Индекс];
		Иначе
			Состав = СтрРазделить(ИмяПоля, ".");
			Псевдоним = Состав[Состав.Количество() - 1];
			ИспользуемыйПсевдоним = ИспользуемыеПсевдонимы[Псевдоним];
			
			Если ИспользуемыйПсевдоним = Неопределено Тогда
				ИспользуемыеПсевдонимы[Псевдоним] = 1;
			Иначе
				ИспользуемыеПсевдонимы[Псевдоним] = ИспользуемыеПсевдонимы[Псевдоним] + 1;
				Псевдоним = Псевдоним + Формат(ИспользуемыеПсевдонимы[Псевдоним], "ЧГ=0");
			КонецЕсли;
		КонецЕсли;
		
		Если ОбщегоНазначенияКлиентСервер.ИмяСоответствуетТребованиямИменованияСвойств(Псевдоним) Тогда
			Псевдоним = " КАК " + Псевдоним;
		Иначе
			Псевдоним = "";
		КонецЕсли;
		
		Имя = ПолноеИмяТаблицы + ИмяПоля + Псевдоним;
		Поля.Добавить(Имя);
	КонецЦикла;
	
	Разделитель = ",
		|	";
	
	Возврат ?(Дополнительные, Разделитель, "") + СтрСоединить(Поля, Разделитель);
	
КонецФункции

// Формирует фрагмент текста запроса с указанным порядком.
//
// Параметры:
//  ИменаПолей - Массив - имена полей в виде массива.
//  Направления - Массив - направления упорядочивания (ВОЗР, УБЫВ) в количестве равном количеству ИменаПолей.
//              - Неопределено - порядок не указывается (всегда ВОЗР).
//  ИмяТаблицы - Строка - имя таблицы в которой расположены поля.
//                        если указана пустая строка, то имя таблицы не подставляется.
//  Дополнительные - Булево - Истина, если это не первые поля в выборке и перед ними нужна ",".
//
// Возвращаемое значение:
//  Строка - фрагмент текста запроса для упорядочивания.
//
Функция УпорядочиванияДляЗапроса(ИменаПолей, Направления = Неопределено, ИмяТаблицы = "", Дополнительные = Ложь)
	
	КоличествоПолей = ИменаПолей.Количество();
	
	Если КоличествоПолей = 0 Тогда
		Возврат "";
	КонецЕсли;
	
	ПолноеИмяТаблицы = ?(ПустаяСтрока(ИмяТаблицы), "", ИмяТаблицы + ".");
	ЕстьНаправления = Направления <> Неопределено И Направления.Количество() = КоличествоПолей;
	Упорядочивание = Новый Массив;
	
	Для Индекс = 0 По КоличествоПолей - 1 Цикл
		ИмяПоля = ИменаПолей[Индекс];
		
		Если ЕстьНаправления Тогда
			ТекущееНаправление = Направления[Индекс];
			Направление = ?(ПустаяСтрока(ТекущееНаправление), "", " " + ТекущееНаправление);
		Иначе
			Направление = "";
		КонецЕсли;
		
		Порядок = ПолноеИмяТаблицы + ИмяПоля + Направление;
		Упорядочивание.Добавить(Порядок);
	КонецЦикла;
	
	Разделитель = ",
		|	";
	
	Возврат ?(Дополнительные, Разделитель, "") + СтрСоединить(Упорядочивание, Разделитель);
	
КонецФункции

// Установить размер выборки запроса, получающего данные для обновления.
//
// Параметры:
//  ТекстЗапроса - Строка - текст модифицируемого запроса.
//  Параметры - см. ОбновлениеИнформационнойБазы.ДополнительныеПараметрыВыборкиДанныхДляОбработки.
//
Процедура УстановитьРазмерВыборки(ТекстЗапроса, Параметры)
	
	РазмерВыборки = ?(Параметры.ВыбиратьПорциями, Параметры.МаксимумВыборки, Неопределено);
	ИзменитьМаксимумВыборки(ТекстЗапроса, 1000, РазмерВыборки);
	
КонецПроцедуры

// Установить размер выборки запроса (ПЕРВЫЕ N).
//
// Параметры:
//  ТекстЗапроса - Строка - текст изменяемого запроса.
//  ТекущееКоличество - Число - количество, установленное в текущем тексте запроса.
//  НовоеКоличество - Число - новое значение для "ПЕРВЫЕ N".
//                  - Неопределено - выбрать все (без ПЕРВЫЕ N).
//
Процедура ИзменитьМаксимумВыборки(ТекстЗапроса, ТекущееКоличество, НовоеКоличество)
	
	ТекстПоиска = "ПЕРВЫЕ " + Формат(ТекущееКоличество, "ЧН=0; ЧГ=0"); // @query-part
	
	Если НовоеКоличество = Неопределено Тогда
		ТекстЗамены = "";
	Иначе
		ТекстЗамены = "ПЕРВЫЕ " + Формат(НовоеКоличество, "ЧН=0; ЧГ=0"); // @query-part
	КонецЕсли;
	
	ТекстЗапроса = СтрЗаменить(ТекстЗапроса, ТекстПоиска, ТекстЗамены);
	
КонецПроцедуры

// Установить поля выборки с учетом многопоточных обработчиков обновления.
//
// Параметры:
//  Запрос - Запрос - модифицируемый запрос.
//  ИменаПолей - Массив - имена полей для выборки.
//  Псевдонимы - Массив - псевдонимы выбираемых полей.
//  ИмяТаблицы - Строка - имя таблицы в которой расположены поля.
//                        если указана пустая строка, то имя таблицы не подставляется.
//
Процедура УстановитьПоляПоСтраницам(Запрос, ПараметрыПостроения)
	
	ВыбираемыеПоля = ПоляДляЗапроса(ПараметрыПостроения.ПоляВыборки,
		ПараметрыПостроения.Псевдонимы,
		ПараметрыПостроения.ИмяТаблицы);
	
	Запрос.Текст = СтрЗаменить(Запрос.Текст, "&ВыбираемыеПоля", ВыбираемыеПоля);
	
КонецПроцедуры

// Установить порядок выборки с учетом многопоточных обработчиков обновления.
//
// Параметры:
//  Запрос - Запрос - модифицируемый запрос.
//  ИменаПолей - Массив - имена полей для выборки.
//  Параметры - см. ОбновлениеИнформационнойБазы.ДополнительныеПараметрыВыборкиДанныхДляОбработки.
//  ИмяТаблицы - Строка - имя таблицы в которой расположены поля.
//                        если указана пустая строка, то имя таблицы не подставляется.
//  Направления - Массив - направления упорядочивания (ВОЗР, УБЫВ) в количестве равном количеству ИменаПолей.
//              - Неопределено - порядок не указывается (всегда ВОЗР).
//
Процедура УстановитьПорядокПоСтраницам(Запрос, ПараметрыПостроения)
	
	ПоляВыборки = ПараметрыПостроения.ПоляВыборки;
	ИмяТаблицы = ПараметрыПостроения.ИмяТаблицы;
	Направления = ПараметрыПостроения.Направления;
	
	Если Направления.Количество() = 0 Тогда
		Направления.Добавить("УБЫВ");
		
		Если ПараметрыПостроения.ПостраничнаяВыборка Тогда
			Для Индекс = 1 По ПоляВыборки.Количество() - 1 Цикл
				Направления.Добавить("");
			КонецЦикла;
		КонецЕсли;
	КонецЕсли;
	
	ПорядокВыборки = УпорядочиванияДляЗапроса(ПоляВыборки, Направления, ИмяТаблицы);
	Запрос.Текст = СтрЗаменить(Запрос.Текст, "&ПорядокВыборки", ПорядокВыборки);
	
КонецПроцедуры

// Позволяет определить постраничная ли это выборка.
//
// Параметры:
//   Параметры - см. ОбновлениеИнформационнойБазы.ДополнительныеПараметрыВыборкиДанныхДляОбработки.
//
// Возвращаемое значение:
//  Булево - Истина, если выборка многопоточная.
//
Функция ЭтоПостраничнаяВыборка(Параметры)
	
	Возврат Параметры.Свойство("СпособВыборки") И Параметры.ВыбиратьПорциями;
	
КонецФункции

// Возвращает, выполняется ли упорядочивание по возрастанию.
//
// Параметры:
//  Направление - Строка - проверяемое направление упорядочивания.
//
// Возвращаемое значение:
//  Булево - Истина, если упорядочивание по возрастанию, иначе Ложь.
//
Функция УпорядочиваниеПоВозрастанию(Направление)
	
	Если Направление = Неопределено Тогда
		Возврат Истина;
	Иначе
		ПорядокВРег = ВРег(Направление);
	
		// НеПереводить: "УБЫВ".
		Возврат НЕ (ПорядокВРег = "УБЫВ" Или ПорядокВРег = "DESC");
	КонецЕсли;
	
КонецФункции

// Возвращает оператор ">" для страничной выборки, с учетом порядка упорядочивания.
//
// Параметры:
//  Направление - Строка - проверяемое направление упорядочивания.
//
// Возвращаемое значение:
//  Строка - оператор ">", если Порядок = "ВОЗР", иначе "<".
//
Функция ОператорБольше(Направление)
	
	Возврат ?(УпорядочиваниеПоВозрастанию(Направление), ">", "<");
	
КонецФункции

// Возвращает оператор "<" для страничной выборки, с учетом порядка упорядочивания.
//
// Параметры:
//  Направление - Строка - проверяемое направление упорядочивания.
//
// Возвращаемое значение:
//  Строка - оператор "<", если Порядок = "ВОЗР", иначе ">".
//
Функция ОператорМеньше(Направление)
	
	Возврат ?(УпорядочиваниеПоВозрастанию(Направление), "<", ">");
	
КонецФункции

// Возвращает оператор ">=" для страничной выборки, с учетом порядка упорядочивания.
//
// Параметры:
//  Направление - Строка - проверяемое направление упорядочивания.
//
// Возвращаемое значение:
//  Строка - оператор ">=", если Порядок = "ВОЗР", иначе "<=".
//
Функция ОператорБольшеИлиРавно(Направление)
	
	Возврат ?(УпорядочиваниеПоВозрастанию(Направление), ">=", "<=");
	
КонецФункции

// Возвращает оператор "<=" для страничной выборки, с учетом порядка упорядочивания.
//
// Параметры:
//  Направление - Строка - проверяемое направление упорядочивания.
//
// Возвращаемое значение:
//  Строка - оператор "<=", если Порядок = "ВОЗР", иначе ">=".
//
Функция ОператорМеньшеИлиРавно(Направление)
	
	Возврат ?(УпорядочиваниеПоВозрастанию(Направление), "<=", ">=");
	
КонецФункции

// Возвращает имя объекта метаданных из его полного имени.
//
// Параметры:
//  ПолноеИмя - Строка - полное имя объекта метаданных.
//
// Возвращаемое значение:
//  Строка - имя объекта метаданных (после точки).
//
Функция ИмяОбъектаМетаданных(ПолноеИмя)
	
	Позиция = СтрНайти(ПолноеИмя, ".", НаправлениеПоиска.СКонца);
	
	Если Позиция > 0  Тогда
		Возврат Сред(ПолноеИмя, Позиция + 1);
	Иначе
		Возврат ПолноеИмя;
	КонецЕсли;
	
КонецФункции

// Возвращает признак наличия полей упорядочивания.
//
// Параметры:
//  
Функция ЗаданыПоляУпорядочивания(ДополнительныеПараметры)
	
	Возврат ДополнительныеПараметры.ПоляУпорядочивания.Количество() > 0;
	
КонецФункции

// Возвращает вид источника данных, см. ДополнительныеПараметрыВыборкиДанныхДляОбработки п. 1 и п. 2.
//
// Параметры:
//  ДополнительныеИсточникиДанных - см. ДополнительныеПараметрыВыборкиДанныхДляОбработки
//
// Возвращаемое значение:
//  Булево - Истина, если это простой набор источников, как в п.1, Ложь - если это иерархия соответствий,
//           как в п. 2.
//
Функция ЭтоПростойИсточникДанных(ДополнительныеИсточникиДанных)
	
	ПростойИсточник = Ложь;
	СложныйИсточник = Ложь;
	ТипСоответствие = Тип("Соответствие");
	
	Для Каждого КлючИЗначение Из ДополнительныеИсточникиДанных Цикл
		Если ТипЗнч(КлючИЗначение.Значение) = ТипСоответствие Тогда
			СложныйИсточник = Истина;
		Иначе
			ПростойИсточник = Истина;
		КонецЕсли;
	КонецЦикла;
	
	Если ПростойИсточник И СложныйИсточник Тогда
		Ошибка = НСтр("ru = 'Источник данных задан неверно (см. %1).'");
		Ошибка = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(Ошибка, "ДополнительныеПараметрыВыборкиДанныхДляОбработки()");
		ВызватьИсключение Ошибка;
	Иначе
		Возврат ПростойИсточник;
	КонецЕсли;
	
КонецФункции

Функция ЭтоПеречисление(ПроверяемыйТип)
	
	Возврат Перечисления.ТипВсеСсылки().СодержитТип(ПроверяемыйТип);
	
КонецФункции

// Добавить в список запросов запросы, уничтожающие указанные временные таблицы.
//
// Параметры:
//  Запросы - Массив - массив, в который добавляются тексты запросов.
//  ВременныеТаблицы - Соответствие
//
Процедура ДобавитьЗапросыУничтоженияВТ(Запросы, ВременныеТаблицы)
	
	ШаблонЗапроса =
		"УНИЧТОЖИТЬ
		|	%1";
	
	Для Каждого ВременнаяТаблица Из ВременныеТаблицы Цикл
		ТекстЗапроса = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(ШаблонЗапроса, ВременнаяТаблица.Значение);
		Запросы.Добавить(ТекстЗапроса);
	КонецЦикла;
	
КонецПроцедуры

// Получить значение коллекции по ключу, а если ключ не найден, создать для ключа значение указанного типа.
//
// Параметры:
//   Коллекция - Соответствие
//             - Структура - коллекция в которой ищется значение.
//   Ключ - Произвольный - ключ по которому ищется значение.
//   Тип - Строка - тип создаваемого значения.
//
// Возвращаемое значение:
//  Соответствие
//
Функция ЗначениеПоКлючу(Коллекция, Ключ, Тип = "Соответствие")
	
	Значение = Коллекция[Ключ];
	
	Если Значение = Неопределено Тогда
		Значение = Новый(Тип);
		Коллекция[Ключ] = Значение;
	КонецЕсли;
	
	Возврат Значение;
	
КонецФункции

// Объединить указанные тексты запросов в пакет.
//
// Параметры:
//  Запросы - Массив - тексты запросов, которые нужно объединить.
//  РазделительЗапросов - Строка
//                      - Неопределено - текст разделителя запросов (по умолчанию, как у конструктора).
//
// Возвращаемое значение:
//  Строка - запросы, объединенные в пакет.
//
Функция ОбъединитьЗапросыВПакет(Запросы, РазделительЗапросов = Неопределено)
	
	Если РазделительЗапросов = Неопределено Тогда
		Разделитель =
			"
			|;
			|
			|";
	Иначе
		Разделитель = РазделительЗапросов;
	КонецЕсли;
	
	Возврат СтрСоединить(Запросы, Разделитель);
	
КонецФункции

// Получить источник данных в виде структуры с полями ТабличнаяЧасть и Реквизит.
//
// Параметры:
//  Источник - Строка - источник данных (путь к реквизиту шапки или табличной части объекта).
//
// Возвращаемое значение:
//  Структура:
//   * ТабличнаяЧасть - Строка - имя табличной части (Неопределено, если источник - реквизит шапки).
//   * Реквизит - Строка - имя реквизита.
//
Функция СоставСсылочногоИсточникаДанных(Источник)
	
	Состав = СтрРазделить(Источник, ".");
	Описание = Новый Структура("ТабличнаяЧасть, Реквизит");
	
	Если Состав.Количество() > 1 Тогда
		Описание.ТабличнаяЧасть = СокрЛП(Состав[0]);
		Описание.Реквизит = СокрЛП(Состав[1]);
	Иначе
		Описание.Реквизит = СокрЛП(Состав[0]);
	КонецЕсли;
	
	Возврат Описание;
	
КонецФункции

// Текст запроса создания временной таблицы со ссылками, заблокированными по реквизитам табличных частей.
//
// Параметры:
//  РеквизитыТЧ - Соответствие из КлючИЗначение:
//   * Ключ - Строка - имя табличной части источника данных.
//   * Значение - Соответствие из КлючИЗначение:
//     ** Ключ - ОбъектМетаданных - объект метаданных, соответствующий типу источника данных.
//     ** Значение - Соответствие из КлючИЗначение:
//        *** Ключ - Строка - имя реквизита табличной части источника данных.
//        *** Значение - Булево - Истина, как значение по умолчанию (не нужно, т.к. по сути - это множество).
//  ПолноеИмяОбъекта - Строка - полное имя обновляемого ссылочного объекта метаданных.
//  ПолноеИмяРегистра - Строка - полное имя обновляемого объекта метаданных регистра.
//  ВременныеТаблицы - Соответствие из КлючИЗначение:
//   * Ключ - ОбъектМетаданных - для которого создана временная таблица.
//   * Значение - Строка - имя временной таблицы.
//
// Возвращаемое значение:
//  Строка - текст запроса.
//
Функция ТекстЗапросаВТЗаблокированныхПоТЧ(РеквизитыТЧ, ПолноеИмяОбъекта, ПолноеИмяРегистра, ВременныеТаблицы)
	
	ШаблонЗапроса =
		"ВЫБРАТЬ
		|	&Реквизит КАК Ссылка
		|ПОМЕСТИТЬ ЗаблокированныеПоТЧ
		|ИЗ
		|	&ТаблицаИзменений КАК ТаблицаИзменений
		|ГДЕ
		|	&Условие
		|ИНДЕКСИРОВАТЬ ПО
		|	Ссылка";
	ШаблонУсловия =
		"ИСТИНА В " + "(" + "
		|		ВЫБРАТЬ ПЕРВЫЕ 1
		|			ИСТИНА
		|		ИЗ
		|			#Таблица КАК ТЧДокументаПолноеИмяОбъекта
		|		ГДЕ
		|			&УсловиеПоПолюТаблицы
		|			И (&ТекстУсловийПоЗаблокированным)" + ")";
	ШаблонУсловияПоЗаблокированным =
		"ИСТИНА В " + "(" + "
		|					ВЫБРАТЬ ПЕРВЫЕ 1
		|						ИСТИНА
		|					ИЗ
		|						#Таблица КАК ВТЗаблокированоТаблица
		|					ГДЕ
		|						&УсловиеПоЗаблокированным" + ")";
	ШаблонУсловияПоРеквизиту = "ТЧДокумента%1.%2 = %3.Ссылка"; // @query-part
	РазделительУсловий =
		"
		|	ИЛИ ";
	РазделительУсловийПоЗаблокированным =
		"
		|				ИЛИ ";
	РазделительУсловийПоРеквизитам =
		"
		|						ИЛИ ";
	
	Если ПолноеИмяРегистра <> Неопределено Тогда
		ПолеТаблицы = "Регистратор";
		Таблица = ПолноеИмяРегистра;
	Иначе
		ПолеТаблицы = "Ссылка";
		Таблица = ПолноеИмяОбъекта;
	КонецЕсли;
	
	Условия = Новый Массив;
	
	Для Каждого ОписаниеТабличнойЧасти Из РеквизитыТЧ Цикл
		ТабличнаяЧасть = ОписаниеТабличнойЧасти.Ключ;
		МетаданныеРеквизитов = ОписаниеТабличнойЧасти.Значение;
		УсловияПоЗаблокированным = Новый Массив;
		
		Для Каждого ОписаниеМетаданных Из МетаданныеРеквизитов Цикл
			МетаданныеИсточника = ОписаниеМетаданных.Ключ;
			Реквизиты = ОписаниеМетаданных.Значение;
			ТаблицаЗаблокированных = ВременныеТаблицы[МетаданныеИсточника];
			УсловияПоРеквизитам = Новый Массив;
			
			Для Каждого ОписаниеРеквизита Из Реквизиты Цикл
				Реквизит = ОписаниеРеквизита.Ключ;
				УсловиеПоРеквизиту = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(ШаблонУсловияПоРеквизиту,
					ТабличнаяЧасть,
					Реквизит,
					ТаблицаЗаблокированных);
				УсловияПоРеквизитам.Добавить(УсловиеПоРеквизиту);
			КонецЦикла;
			
			ТекстУсловийПоРеквизитам = СтрСоединить(УсловияПоРеквизитам, РазделительУсловийПоРеквизитам);
			УсловиеПоЗаблокированным = СтрЗаменить(ШаблонУсловияПоЗаблокированным, "#Таблица", ТаблицаЗаблокированных);
			УсловиеПоЗаблокированным = СтрЗаменить(УсловиеПоЗаблокированным, "ВТЗаблокированоТаблица", ТаблицаЗаблокированных);
			УсловиеПоЗаблокированным = СтрЗаменить(УсловиеПоЗаблокированным, "&УсловиеПоЗаблокированным", ТекстУсловийПоРеквизитам);
			УсловияПоЗаблокированным.Добавить(УсловиеПоЗаблокированным);
		КонецЦикла;
		
		ТекстУсловийПоЗаблокированным = СтрСоединить(УсловияПоЗаблокированным, РазделительУсловийПоЗаблокированным);
		Условие = СтрЗаменить(ШаблонУсловия, "#Таблица", ПолноеИмяОбъекта + "." + ТабличнаяЧасть);
		Условие = СтрЗаменить(Условие, "ТЧДокументаПолноеИмяОбъекта", "ТЧДокумента" + ТабличнаяЧасть);
		Условие = СтрЗаменить(Условие, "&УсловиеПоПолюТаблицы", "ТаблицаИзменений." + ПолеТаблицы + " = ТЧДокумента"+ ТабличнаяЧасть + ".Ссылка");
		Условие = СтрЗаменить(Условие, "&ТекстУсловийПоЗаблокированным", ТекстУсловийПоЗаблокированным);
		Условия.Добавить(Условие);
	КонецЦикла;
	
	ТекстУсловий = СтрСоединить(Условия, РазделительУсловий);
	
	ТекстЗапроса = СтрЗаменить(ШаблонЗапроса, "&Реквизит", ПолеТаблицы);
	ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&ТаблицаИзменений", Таблица + ".Изменения");
	ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&Условие", ТекстУсловий);
	
	Возврат ТекстЗапроса;
	
КонецФункции

// Текст условия запроса для исключения из обновления данных, заблокированных по регистру.
//
// Параметры:
//  ПолноеИмяРегистра - Строка - полное имя обновляемого объекта метаданных регистра.
//  ВременныеТаблицы - Соответствие из КлючИЗначение:
//   * Ключ - ОбъектМетаданных - для которого создана временная таблица.
//   * Значение - Строка - имя временной таблицы.
//
// Возвращаемое значение:
//  Строка - текст запроса.
//
Функция ТекстУсловияЗаблокированныхПоРегистрам(ПолноеИмяРегистра, ВременныеТаблицы)
	
	ШаблонУсловия =
		"НЕ ИСТИНА В (
		|		ВЫБРАТЬ ПЕРВЫЕ 1
		|			ИСТИНА
		|		ИЗ
		|			%1 КАК %1
		|		ГДЕ
		|			%2 = %1.Регистратор)";
	РазделительУсловий =
		"
		|	И ";
	Поле = ?(ПолноеИмяРегистра <> Неопределено, "ТаблицаРегистраИзменения.Регистратор", "ТаблицаОбъекта.Ссылка");
	Условия = Новый Массив;
	
	Для Каждого ОписаниеВременнойТаблицы Из ВременныеТаблицы Цикл
		ВременнаяТаблица = ОписаниеВременнойТаблицы.Значение;
		Условие = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(ШаблонУсловия, ВременнаяТаблица, Поле);
		Условия.Добавить(Условие);
	КонецЦикла;
	
	Возврат СтрСоединить(Условия, РазделительУсловий);
	
КонецФункции

// Текст условия запроса для исключения из обновления данных, заблокированных по реквизитам табличных частей.
//
// Параметры:
//  ПолноеИмяОбъекта - Строка - полное имя обновляемого ссылочного объекта метаданных.
//  ПолноеИмяРегистра - Строка - полное имя обновляемого объекта метаданных регистра.
//
// Возвращаемое значение:
//  Строка - текст запроса.
//
Функция ТекстУсловияЗаблокированныхПоТЧ(ПолноеИмяОбъекта, ПолноеИмяРегистра)
	
	Поле = ?(ПолноеИмяРегистра <> Неопределено, "ТаблицаРегистраИзменения.Регистратор", "ТаблицаИзменений.Ссылка");
	ШаблонУсловия =
		"НЕ ИСТИНА В (
		|		ВЫБРАТЬ ПЕРВЫЕ 1
		|			ИСТИНА
		|		ИЗ
		|			ЗаблокированныеПоТЧ КАК ЗаблокированныеПоТЧ
		|		ГДЕ
		|			%1 = ЗаблокированныеПоТЧ.Ссылка)";
	
	Возврат СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(ШаблонУсловия, Поле);
	
КонецФункции

// Текст условия запроса для исключения из обновления данных, заблокированных по реквизитам шапки объекта.
//
// Параметры:
//  РеквизитыШапки - Соответствие из КлючИЗначение:
//   * Ключ - ОбъектМетаданных - объект метаданных, соответствующий типу источника данных.
//   * Значение - Соответствие из КлючИЗначение:
//      ** Ключ - Строка - имя реквизита табличной части источника данных.
//      ** Значение - Булево - Истина, как значение по умолчанию (не нужно, т.к. по сути - это множество).
//  ПолноеИмяРегистра - Строка - полное имя обновляемого объекта метаданных регистра.
//  ВременныеТаблицы - Соответствие из КлючИЗначение:
//   * Ключ - ОбъектМетаданных - для которого создана временная таблица.
//   * Значение - Строка - имя временной таблицы.
//
// Возвращаемое значение:
//  Строка - текст запроса.
//
Функция ТекстУсловияЗаблокированныхПоШапке(РеквизитыШапки, ПолноеИмяРегистра, ВременныеТаблицы)
	
	ШаблонУсловия =
		"НЕ ИСТИНА В (
		|		ВЫБРАТЬ ПЕРВЫЕ 1
		|			ИСТИНА
		|		ИЗ
		|			%1 КАК %1
		|		ГДЕ
		|			%2)";
	ШаблонУсловияПоРеквизиту = "%1.%2 = %3.Ссылка";
	РазделительУсловий =
		"
		|	И ";
	РазделительУсловийПоРеквизитам =
		"
		|			ИЛИ ";
	Таблица = ?(ПолноеИмяРегистра <> Неопределено, "ТаблицаДокумента", "ТаблицаОбъекта");
	Условия = Новый Массив;
	
	Для Каждого ОписаниеМетаданных Из РеквизитыШапки Цикл
		МетаданныеИсточника = ОписаниеМетаданных.Ключ;
		ТаблицаЗаблокированных = ВременныеТаблицы[МетаданныеИсточника];
		Реквизиты = ОписаниеМетаданных.Значение;
		УсловияПоРеквизитам = Новый Массив;
		
		Для Каждого ОписаниеРеквизита Из Реквизиты Цикл
			Реквизит = ОписаниеРеквизита.Ключ;
			УсловиеПоРеквизиту = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(ШаблонУсловияПоРеквизиту,
				Таблица,
				Реквизит,
				ТаблицаЗаблокированных);
			УсловияПоРеквизитам.Добавить(УсловиеПоРеквизиту);
		КонецЦикла;
		
		ТекстУсловийПоРеквизитам = СтрСоединить(УсловияПоРеквизитам, РазделительУсловийПоРеквизитам);
		Условие = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(ШаблонУсловия,
			ТаблицаЗаблокированных,
			ТекстУсловийПоРеквизитам);
		Условия.Добавить(Условие);
	КонецЦикла;
	
	Возврат СтрСоединить(Условия, РазделительУсловий);
	
КонецФункции

// Формирует условие для отбора только не заблокированных комбинаций измерений.
//
// Параметры:
//  Измерения - Массив - имена измерений, по которым нужно сделать отбор.
//
// Возвращаемое значение:
//  Строка - текст условия отбора.
//
Функция УсловиеОтбораНезаблокированныхИзмерений(Измерения)
	
	УсловияНаИзмерения = Новый Массив;
	ШаблонУсловияНаИзмерение =
		"(ТаблицаИзменений.%1 = ВТЗаблокированоИзмерения.%1
		|				ИЛИ ТаблицаИзменений.%1 = &ПустоеЗначениеИзмерения%1
		|				ИЛИ ВТЗаблокированоИзмерения.%1 = &ПустоеЗначениеИзмерения%1)";
	ШаблонУсловияПоОтборуНезаблокированных =
		"	НЕ ИСТИНА В (
		|		ВЫБРАТЬ ПЕРВЫЕ 1
		|			ИСТИНА
		|		ИЗ
		|			#ВТЗаблокированоИзмерения КАК ВТЗаблокированоИзмерения
		|		ГДЕ
		|			%1)";
	РазделительИ =
		"
		|			И ";
	
	Для Каждого Измерение Из Измерения Цикл
		Условие = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(ШаблонУсловияНаИзмерение, Измерение);
		УсловияНаИзмерения.Добавить(Условие);
	КонецЦикла;
	
	УсловияПоВсемИзмерениям = СтрСоединить(УсловияНаИзмерения, РазделительИ);
	Возврат СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(ШаблонУсловияПоОтборуНезаблокированных,
		УсловияПоВсемИзмерениям);
	
КонецФункции

Функция ОбъектРегистрируетсяНаПланеОбмена(Данные)
	
	ТипОбъекта = ТипЗнч(Данные);
	
	Кеш = ОбновлениеИнформационнойБазыСлужебныйПовтИсп.КешПроверкиЗарегистрированныхОбъектов();
	ВходитВСостав = Кеш.Получить(ТипОбъекта);
	Если ВходитВСостав <> Неопределено Тогда
		Возврат ВходитВСостав;
	КонецЕсли;
	
	ОбъектМетаданных = Метаданные.НайтиПоТипу(ТипОбъекта);
	Если ОбъектМетаданных = Неопределено Тогда
		Кеш.Вставить(ТипОбъекта, Ложь);
		Возврат Ложь;
	КонецЕсли;
	
	СоставПланаОбмена = Метаданные.ПланыОбмена.ОбновлениеИнформационнойБазы.Состав;
	ВходитВСостав = СоставПланаОбмена.Содержит(ОбъектМетаданных);
	Кеш.Вставить(ТипОбъекта, ВходитВСостав);
	
	Возврат ВходитВСостав;
	
КонецФункции

Процедура СброситьСостояниеОбработчика(Обработчик)
	
	ОбработчикОбновления = РегистрыСведений.ОбработчикиОбновления.СоздатьМенеджерЗаписи();
	ОбработчикОбновления.ИмяОбработчика = Обработчик;
	ОбработчикОбновления.Прочитать();
	
	ОбработчикОбновления.Статус = Перечисления.СтатусыОбработчиковОбновления.НеВыполнялся;
	ОбработчикОбновления.ДлительностьОбработки = 0;
	ОбработчикОбновления.ЧислоПопыток = 0;
	ОбработчикОбновления.СтатистикаВыполнения = Новый ХранилищеЗначения(Новый Соответствие);
	ОбработчикОбновления.ИнформацияОбОшибке = "";
	ОбработчикОбновления.ОбработкаПорцииЗавершена = Истина;
	
	ОбработчикОбновления.Записать();
	
КонецПроцедуры

Процедура ЗаписатьПрогрессВыполненияОбработчика(Данные, Узел, МетаданныеОбъекта = Неопределено)
	
	ОбъектовОбработано = 0;
	Если (ТипЗнч(Данные) = Тип("Массив")
		Или ТипЗнч(Данные) = Тип("ТаблицаЗначений")) Тогда
		ОбъектовОбработано = Данные.Количество();
	Иначе
		Если МетаданныеОбъекта = Неопределено Тогда
			ТипЗначенияОбъекта = ТипЗнч(Данные);
			МетаданныеОбъекта  = Метаданные.НайтиПоТипу(ТипЗначенияОбъекта);
		КонецЕсли;
		Если ОбщегоНазначения.ЭтоОбъектСсылочногоТипа(МетаданныеОбъекта) Тогда
			ПолноеИмя = МетаданныеОбъекта.ПолноеИмя();
			Запрос = Новый Запрос;
			Запрос.УстановитьПараметр("Узел", Узел);
			Запрос.УстановитьПараметр("Ссылка", Данные.Ссылка);
			Запрос.Текст = 
				"ВЫБРАТЬ ПЕРВЫЕ 1
				|	ИСТИНА
				|ИЗ
				|	&ИмяТаблицыИзменений КАК ТаблицаИзменения
				|ГДЕ
				|	ТаблицаИзменения.Узел = &Узел
				|	И ТаблицаИзменения.Ссылка = &Ссылка";
			Запрос.Текст = СтрЗаменить(Запрос.Текст, "&ИмяТаблицыИзменений", ПолноеИмя + ".Изменения"); // @query-part
			Если Не Запрос.Выполнить().Пустой() Тогда
				ОбъектовОбработано = 1;
			КонецЕсли;
		Иначе
			Если Данные.Количество() = 0 Тогда
				ОбъектовОбработано = 1;
			Иначе
				ОбъектовОбработано = Данные.Количество();
			КонецЕсли;
		КонецЕсли;
	КонецЕсли;
	
	Если ОбъектовОбработано = 0 Тогда
		Возврат;
	КонецЕсли;
	
	НовыеПараметрыСеанса = ОбновлениеИнформационнойБазыСлужебный.НовыеПараметрыОбработчикаОбновления();
	ЗаполнитьЗначенияСвойств(НовыеПараметрыСеанса, ПараметрыСеанса.ПараметрыОбработчикаОбновления);
	НовыеПараметрыСеанса.ОбработаноОбъектов = НовыеПараметрыСеанса.ОбработаноОбъектов + ОбъектовОбработано;
	
	Если НовыеПараметрыСеанса.ДатаПоследнейЗаписиПрогресса = Неопределено
		Или ТекущаяДатаСеанса() - НовыеПараметрыСеанса.ДатаПоследнейЗаписиПрогресса > 15 Тогда // Больше 15 секунд с последней записи прогресса.
		ОбновлениеИнформационнойБазыСлужебный.ЗаписатьПрогрессВыполненияОбработчика(НовыеПараметрыСеанса);
		
		НовыеПараметрыСеанса.ОбработаноОбъектов = 0;
		НовыеПараметрыСеанса.ДатаПоследнейЗаписиПрогресса = ТекущаяДатаСеанса();
	КонецЕсли;
	
	ПараметрыСеанса.ПараметрыОбработчикаОбновления = Новый ФиксированнаяСтруктура(НовыеПараметрыСеанса);
	
КонецПроцедуры

Процедура УстановитьПараметрыОбработчикаПриВыборкеДанных(ДополнительныеПараметры, ОбработаныВсеАктуальныеДанные, ПараметрыОбработчикаОбновления, ПолноеИмяДокумента = Неопределено)
	
	Если ПараметрыОбработчикаОбновления = Неопределено Тогда
		ПараметрыОбработчика = ПараметрыСеанса.ПараметрыОбработчикаОбновления;
	Иначе
		ПараметрыОбработчика = ПараметрыОбработчикаОбновления;
	КонецЕсли;
	
	ПараметрыОбработчика = Новый Структура(ПараметрыОбработчика);
	Если ПолноеИмяДокумента = Неопределено Тогда
		ПараметрыОбработчика.ОбработаныАктуальныеДанные = ОбработаныВсеАктуальныеДанные;
	Иначе
		Если ПараметрыОбработчика.ОбработанныеТаблицыРегистраторов = Неопределено Тогда
			ОбработанныеТаблицы = ОбщегоНазначенияКлиентСервер.ЗначениеВМассиве(ПолноеИмяДокумента);
		Иначе
			ОбработанныеТаблицы = Новый Массив(ПараметрыОбработчика.ОбработанныеТаблицыРегистраторов);
			ОбработанныеТаблицы.Добавить(ПолноеИмяДокумента);
		КонецЕсли;
		ОбработанныеТаблицы = Новый ФиксированныйМассив(ОбработанныеТаблицы);
		ПараметрыОбработчика.ОбработанныеТаблицыРегистраторов = ОбработанныеТаблицы;
	КонецЕсли;
	
	Если ПараметрыОбработчикаОбновления = Неопределено Тогда
		ПараметрыСеанса.ПараметрыОбработчикаОбновления = Новый ФиксированнаяСтруктура(ПараметрыОбработчика);
	Иначе
		ДополнительныеПараметры.ПараметрыОбработчикаОбновления = Новый ФиксированнаяСтруктура(ПараметрыОбработчика);
	КонецЕсли;
	
КонецПроцедуры

#КонецОбласти